From 7dadf2ee93db6d910c83b34173663e79a79858bd Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 23 May 2026 14:00:51 +1000 Subject: [PATCH 01/68] Remove System.Reactive/DynamicData from core; fused sinks, MinVer, SonarCloud (WIP) Large work-in-progress branch: replaces System.Reactive operator chains with hand-written allocation-tuned fused sinks, removes the transitive System.Reactive dependency (local Splat project-reference), normalizes files to UTF-8 (no BOM), adds a HashCode polyfill and ISubject shim for the legacy/compat surface, swaps versioning to MinVer, and adds the SonarCloud workflow. --- .editorconfig | 1675 +- .github/workflows/ci-build.yml | 2 + .github/workflows/release.yml | 2 + .github/workflows/sonarcloud.yml | 35 + CLAUDE.md | 383 +- CONTRIBUTING_PERFORMANCE.md | 421 + MERGE_FOLLOWUPS.md | 46 + README.md | 132 +- agent.md | 377 +- src/.nuget/NuGet.Config | 2 +- src/Benchmarks/AutoPersistBenchmark.cs | 2 +- src/Benchmarks/CreateReactiveListBenchmark.cs | 2 +- src/Benchmarks/Directory.Packages.props | 2 +- .../INPCObservableForPropertyBenchmarks.cs | 2 +- src/Benchmarks/Mocks/MockHostScreen.cs | 2 +- src/Benchmarks/Mocks/MockViewModel.cs | 2 +- src/Benchmarks/NavigationStackBenchmark.cs | 2 +- src/Benchmarks/Program.cs | 2 +- .../ReactiveCommandCreateBenchmark.cs | 2 +- .../ReactiveListOperationBenchmark.cs | 2 +- src/Benchmarks/ReactiveUI.Benchmarks.sln | 2 +- .../RoutableViewModelMixinsBenchmarks.cs | 2 +- src/Benchmarks/xunit.runner.json | 2 +- src/Directory.Build.props | 38 +- src/Directory.Packages.props | 160 +- .../AndroidXReactiveUIBuilderExtensions.cs | 2 +- .../ControlFetcherMixin.cs | 31 +- .../ReactiveAppCompatActivity.cs | 42 +- .../ReactiveAppCompatActivity{TViewModel}.cs | 5 +- .../ReactiveDialogFragment.cs | 18 +- .../ReactiveDialogFragment{TViewModel}.cs | 5 +- src/ReactiveUI.AndroidX/ReactiveFragment.cs | 12 +- .../ReactiveFragmentActivity.cs | 42 +- .../ReactiveFragmentActivity{TViewModel}.cs | 5 +- .../ReactiveFragment{TViewModel}.cs | 5 +- .../ReactivePagerAdapter.cs | 30 +- ...ivePagerAdapter{TViewModel,TCollection}.cs | 11 +- .../ReactivePreferenceFragment.cs | 19 +- .../ReactivePreferenceFragment{TViewModel}.cs | 8 +- .../ReactiveRecyclerViewAdapter.cs | 67 +- ...clerViewAdapter{TViewModel,TCollection}.cs | 8 +- .../ReactiveRecyclerViewViewHolder.cs | 56 +- .../ReactiveUI.AndroidX.csproj | 38 +- src/ReactiveUI.AndroidX/Registrations.cs | 8 +- .../BlazorReactiveUIBuilderExtensions.cs | 2 +- src/ReactiveUI.Blazor/GlobalUsings.cs | 2 +- .../Internal/ReactiveComponentHelpers.cs | 53 +- .../Internal/ReactiveComponentState.cs | 21 +- src/ReactiveUI.Blazor/PlatformOperations.cs | 2 +- .../Properties/launchSettings.json | 48 +- .../ReactiveComponentBase.cs | 9 +- .../ReactiveInjectableComponentBase.cs | 11 +- .../ReactiveLayoutComponentBase.cs | 12 +- .../ReactiveOwningComponentBase.cs | 23 +- .../ReactiveUI.Blazor.csproj | 11 +- src/ReactiveUI.Blazor/Registrations.cs | 8 +- .../FollowObservableStateBehavior.cs | 19 +- src/ReactiveUI.Blend/ObservableTrigger.cs | 34 +- .../Properties/ReactiveUI.Blend_UWP.rd.xml | 2 +- src/ReactiveUI.Blend/ReactiveUI.Blend.csproj | 24 +- .../ReactiveUIBuilderDrawingExtensions.cs | 4 +- .../ReactiveUI.Drawing.csproj | 22 +- src/ReactiveUI.Drawing/Registrations.cs | 4 +- .../ActivationForViewFetcher.cs | 124 +- src/ReactiveUI.Maui/AutoSuspendHelper.cs | 28 +- .../MauiReactiveUIBuilderExtensions.cs | 87 +- .../Common/AutoDataTemplateBindingHook.cs | 16 +- .../Common/BooleanToVisibilityHint.cs | 8 +- .../BooleanToVisibilityTypeConverter.cs | 8 +- .../Common/PlatformOperations.cs | 2 +- src/ReactiveUI.Maui/Common/ReactivePage.cs | 35 +- .../Common/ReactiveUserControl.cs | 2 +- src/ReactiveUI.Maui/Common/RoutedViewHost.cs | 2 +- .../Common/ViewModelViewHost.cs | 2 +- .../VisibilityToBooleanTypeConverter.cs | 8 +- .../DisableAnimationAttribute.cs | 2 +- .../Internal/MauiReactiveHelpers.cs | 18 +- src/ReactiveUI.Maui/ReactiveCarouselView.cs | 26 +- src/ReactiveUI.Maui/ReactiveContentPage.cs | 26 +- src/ReactiveUI.Maui/ReactiveContentView.cs | 26 +- src/ReactiveUI.Maui/ReactiveEntryCell.cs | 32 +- src/ReactiveUI.Maui/ReactiveFlyoutPage.cs | 26 +- src/ReactiveUI.Maui/ReactiveImageCell.cs | 32 +- src/ReactiveUI.Maui/ReactiveImageItemView.cs | 187 +- .../ReactiveMasterDetailPage.cs | 26 +- src/ReactiveUI.Maui/ReactiveMultiPage.cs | 29 +- src/ReactiveUI.Maui/ReactiveNavigationPage.cs | 26 +- src/ReactiveUI.Maui/ReactiveShell.cs | 25 +- src/ReactiveUI.Maui/ReactiveShellContent.cs | 60 +- src/ReactiveUI.Maui/ReactiveSwitchCell.cs | 33 +- src/ReactiveUI.Maui/ReactiveTabbedPage.cs | 26 +- src/ReactiveUI.Maui/ReactiveTextCell.cs | 32 +- src/ReactiveUI.Maui/ReactiveTextItemView.cs | 143 +- src/ReactiveUI.Maui/ReactiveUI.Maui.csproj | 54 +- src/ReactiveUI.Maui/ReactiveViewCell.cs | 32 +- src/ReactiveUI.Maui/Registrations.cs | 2 +- src/ReactiveUI.Maui/RoutedViewHost.cs | 400 +- .../RoutedViewHost{TViewModel}.cs | 363 +- src/ReactiveUI.Maui/ViewModelViewHost.cs | 91 +- .../ViewModelViewHost{TViewModel}.cs | 46 +- .../DependencyObjectObservableForProperty.cs | 48 +- .../WinUI/DispatcherQueueScheduler.cs | 2 +- .../TransitioningContentControl.Empty.cs | 2 +- .../ReactiveUI.Testing.Reactive.csproj | 14 +- .../TestSchedulerExtensions.cs | 9 +- src/ReactiveUI.Testing/AppBuilderTestBase.cs | 2 +- src/ReactiveUI.Testing/IBuilder.cs | 7 +- src/ReactiveUI.Testing/IBuilderExtensions.cs | 3 +- .../MessageBusExtensions.cs | 35 +- .../Properties/ReactiveUI.Testing.rd.xml | 2 +- .../ReactiveUI.Testing.csproj | 26 +- src/ReactiveUI.Testing/RxTest.cs | 20 +- src/ReactiveUI.Testing/SchedulerExtensions.cs | 34 +- src/ReactiveUI.Testing/TestSequencer.cs | 42 +- .../WinUIReactiveUIBuilderExtensions.cs | 2 +- src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj | 51 +- .../ActivationForViewFetcher.cs | 137 +- .../WinFormsReactiveUIBuilderExtensions.cs | 15 +- .../ContentControlBindingHook.cs | 13 +- .../CreatesWinformsCommandBinding.cs | 138 +- ...llectionChangedToListChangedTransformer.cs | 29 +- .../PanelSetMethodBindingConverter.cs | 17 +- src/ReactiveUI.Winforms/PlatformOperations.cs | 2 +- .../ReactiveUI.Winforms.csproj | 41 +- .../ReactiveUserControl.Designer.cs | 2 +- .../ReactiveUserControl.cs | 2 +- .../ReactiveUserControlNonGeneric.Designer.cs | 2 +- .../ReactiveUserControlNonGeneric.cs | 2 +- src/ReactiveUI.Winforms/Registrations.cs | 14 +- .../RoutedViewHost.Designer.cs | 2 +- src/ReactiveUI.Winforms/RoutedViewHost.cs | 101 +- .../TableContentSetMethodBindingConverter.cs | 30 +- .../ViewModelViewHost.Designer.cs | 2 +- src/ReactiveUI.Winforms/ViewModelViewHost.cs | 166 +- .../WinformsCreatesObservableForProperty.cs | 95 +- .../ActivationForViewFetcher.cs | 33 +- src/ReactiveUI.Wpf/Attributes.cs | 18 +- src/ReactiveUI.Wpf/AutoSuspendHelper.cs | 52 +- .../Binding/ValidationBindingMixins.cs | 17 +- .../Binding/ValidationBindingWpf.cs | 87 +- .../Builder/WpfReactiveUIBuilderExtensions.cs | 13 +- .../Common/AutoDataTemplateBindingHook.cs | 23 +- .../Common/BooleanToVisibilityHint.cs | 6 +- .../BooleanToVisibilityTypeConverter.cs | 9 +- .../Common/PlatformOperations.cs | 2 +- src/ReactiveUI.Wpf/Common/ReactivePage.cs | 21 +- .../Common/ReactiveUserControl.cs | 20 +- src/ReactiveUI.Wpf/Common/RoutedViewHost.cs | 60 +- .../Common/ViewModelViewHost.cs | 63 +- .../VisibilityToBooleanTypeConverter.cs | 9 +- .../DependencyObjectObservableForProperty.cs | 57 +- src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj | 41 +- src/ReactiveUI.Wpf/ReactiveWindow.cs | 20 +- src/ReactiveUI.Wpf/Registrations.cs | 10 +- .../Rx/Concurrency/ControlScheduler.cs | 2 +- .../Rx/Concurrency/DispatcherScheduler.cs | 2 +- src/ReactiveUI.Wpf/Rx/Internal/Constants.cs | 2 +- .../Rx/Linq/ControlObservable.cs | 2 +- .../Rx/Linq/DispatcherObservable.cs | 2 +- .../Rx/Linq/Observable.Remoting.cs | 14 +- .../Rx/Linq/QueryLanguage.Remoting.cs | 11 +- src/ReactiveUI.Wpf/Themes/Generic.xaml | 60 +- .../TransitioningContentControl.cs | 186 +- .../WpfCommandRebindingCustomizer.cs | 22 +- .../Activation/CanActivateViewFetcher.cs | 145 +- .../Activation/IActivationForViewFetcher.cs | 2 +- src/ReactiveUI/Activation/ViewForMixins.cs | 201 +- .../Activation/ViewModelActivator.cs | 45 +- src/ReactiveUI/Bindings/BindingAffinity.cs | 26 + src/ReactiveUI/Bindings/BindingDirection.cs | 4 +- .../Bindings/BindingTypeConverter.cs | 6 +- .../Bindings/BindingTypeConverterDispatch.cs | 76 +- .../Bindings/Command/CommandBinder.cs | 236 +- .../Command/CommandBinderImplementation.cs | 419 +- .../CommandBinderImplementationMixins.cs | 21 +- .../Bindings/Command/CreatesCommandBinding.cs | 46 +- ...reatesCommandBindingViaCommandParameter.cs | 375 +- .../Command/CreatesCommandBindingViaEvent.cs | 140 +- .../Command/ICommandBinderImplementation.cs | 39 +- .../Converter/BooleanToStringTypeConverter.cs | 4 +- .../ByteToNullableByteTypeConverter.cs | 4 +- .../Converter/ByteToStringTypeConverter.cs | 32 +- .../DateOnlyToStringTypeConverter.cs | 5 +- .../DateTimeOffsetToStringTypeConverter.cs | 4 +- .../DateTimeToStringTypeConverter.cs | 6 +- .../DecimalToNullableDecimalTypeConverter.cs | 4 +- .../Converter/DecimalToStringTypeConverter.cs | 32 +- .../DoubleToNullableDoubleTypeConverter.cs | 4 +- .../Converter/DoubleToStringTypeConverter.cs | 32 +- .../Converter/EqualityTypeConverter.cs | 3 +- .../Converter/GuidToStringTypeConverter.cs | 4 +- .../IntegerToNullableIntegerTypeConverter.cs | 4 +- .../Converter/IntegerToStringTypeConverter.cs | 32 +- .../LongToNullableLongTypeConverter.cs | 4 +- .../Converter/LongToStringTypeConverter.cs | 32 +- .../NullableBooleanToStringTypeConverter.cs | 6 +- .../NullableByteToByteTypeConverter.cs | 39 +- .../NullableByteToStringTypeConverter.cs | 34 +- .../NullableDateOnlyToStringTypeConverter.cs | 7 +- ...ableDateTimeOffsetToStringTypeConverter.cs | 9 +- .../NullableDateTimeToStringTypeConverter.cs | 8 +- .../NullableDecimalToDecimalTypeConverter.cs | 39 +- .../NullableDecimalToStringTypeConverter.cs | 34 +- .../NullableDoubleToDoubleTypeConverter.cs | 41 +- .../NullableDoubleToStringTypeConverter.cs | 34 +- .../NullableGuidToStringTypeConverter.cs | 6 +- .../NullableIntegerToIntegerTypeConverter.cs | 39 +- .../NullableIntegerToStringTypeConverter.cs | 34 +- .../NullableLongToLongTypeConverter.cs | 39 +- .../NullableLongToStringTypeConverter.cs | 34 +- .../NullableShortToShortTypeConverter.cs | 39 +- .../NullableShortToStringTypeConverter.cs | 34 +- .../NullableSingleToSingleTypeConverter.cs | 39 +- .../NullableSingleToStringTypeConverter.cs | 34 +- .../NullableTimeOnlyToStringTypeConverter.cs | 7 +- .../NullableTimeSpanToStringTypeConverter.cs | 6 +- .../ShortToNullableShortTypeConverter.cs | 4 +- .../Converter/ShortToStringTypeConverter.cs | 32 +- .../SingleToNullableSingleTypeConverter.cs | 4 +- .../Converter/SingleToStringTypeConverter.cs | 34 +- .../Bindings/Converter/StringConverter.cs | 32 +- .../Converter/StringToBooleanTypeConverter.cs | 8 +- .../Converter/StringToByteTypeConverter.cs | 8 +- .../StringToDateOnlyTypeConverter.cs | 7 +- .../StringToDateTimeOffsetTypeConverter.cs | 6 +- .../StringToDateTimeTypeConverter.cs | 6 +- .../Converter/StringToDecimalTypeConverter.cs | 8 +- .../Converter/StringToDoubleTypeConverter.cs | 8 +- .../Converter/StringToGuidTypeConverter.cs | 14 +- .../Converter/StringToIntegerTypeConverter.cs | 8 +- .../Converter/StringToLongTypeConverter.cs | 8 +- .../StringToNullableBooleanTypeConverter.cs | 6 +- .../StringToNullableByteTypeConverter.cs | 6 +- .../StringToNullableDateOnlyTypeConverter.cs | 7 +- ...ngToNullableDateTimeOffsetTypeConverter.cs | 9 +- .../StringToNullableDateTimeTypeConverter.cs | 6 +- .../StringToNullableDecimalTypeConverter.cs | 6 +- .../StringToNullableDoubleTypeConverter.cs | 6 +- .../StringToNullableGuidTypeConverter.cs | 6 +- .../StringToNullableIntegerTypeConverter.cs | 6 +- .../StringToNullableLongTypeConverter.cs | 6 +- .../StringToNullableShortTypeConverter.cs | 6 +- .../StringToNullableSingleTypeConverter.cs | 6 +- .../StringToNullableTimeOnlyTypeConverter.cs | 7 +- .../StringToNullableTimeSpanTypeConverter.cs | 6 +- .../Converter/StringToShortTypeConverter.cs | 8 +- .../Converter/StringToSingleTypeConverter.cs | 8 +- .../StringToTimeOnlyTypeConverter.cs | 7 +- .../StringToTimeSpanTypeConverter.cs | 8 +- .../Converter/StringToUriTypeConverter.cs | 4 +- .../TimeOnlyToStringTypeConverter.cs | 5 +- .../TimeSpanToStringTypeConverter.cs | 4 +- .../Converter/UriToStringTypeConverter.cs | 4 +- .../BindingFallbackConverterRegistry.cs | 18 +- .../BindingTypeConverterRegistry.cs | 40 +- .../Converters/ConverterMigrationHelper.cs | 16 +- .../Bindings/Converters/ConverterService.cs | 34 +- .../Bindings/Converters/RxConverters.cs | 2 +- .../SetMethodBindingConverterRegistry.cs | 18 +- .../Bindings/IBindingFallbackConverter.cs | 13 +- .../Bindings/IBindingTypeConverter.cs | 2 +- .../IBindingTypeConverter{TFrom,TTo}.cs | 6 +- .../Bindings/ISetMethodBindingConverter.cs | 2 +- .../IInteractionBinderImplementation.cs | 4 +- .../InteractionBinderImplementation.cs | 322 +- .../Interaction/InteractionBindingMixins.cs | 23 +- .../Property/IPropertyBinderImplementation.cs | 404 +- .../Internal/BindingConverterResolver.cs | 121 +- .../Property/Internal/BindingHookEvaluator.cs | 18 +- .../Internal/IBindingConverterResolver.cs | 2 +- .../Internal/IBindingHookEvaluator.cs | 4 +- .../IPropertyBindingExpressionCompiler.cs | 12 +- .../PropertyBindingExpressionCompiler.cs | 409 +- ...ropertyBinderImplementation.Conversions.cs | 332 + .../Property/PropertyBinderImplementation.cs | 1164 +- .../Property/PropertyBindingMixins.cs | 558 +- .../Bindings/Property/TriggerUpdate.cs | 2 +- .../Bindings/Reactive/IReactiveBinding.cs | 4 +- .../Bindings/Reactive/ReactiveBinding.cs | 15 +- src/ReactiveUI/Builder/IReactiveUIBuilder.cs | 91 +- src/ReactiveUI/Builder/IReactiveUIInstance.cs | 3 +- src/ReactiveUI/Builder/ReactiveUIBuilder.cs | 519 +- src/ReactiveUI/Builder/RxAppBuilder.cs | 19 +- .../ChangeSets/ChangeSetExtensions.cs | 308 + .../ChangeSets/CollectionChanged.cs | 53 + .../ChangeSets/CollectionChangedExtensions.cs | 68 + .../ChangeSets/IReactiveChangeSet.cs | 20 + .../ChangeSets/IReactiveChangeSet{T}.cs | 12 + src/ReactiveUI/ChangeSets/ReactiveChange.cs | 70 + .../ChangeSets/ReactiveChangeReason.cs | 28 + src/ReactiveUI/Comparers/ChainedComparer.cs | 2 +- .../Comparers/ComparerChainingExtensions.cs | 20 +- src/ReactiveUI/Comparers/IComparerBuilder.cs | 2 +- src/ReactiveUI/Comparers/OrderedComparer.cs | 80 +- .../Comparers/OrderedComparer{T}.cs | 71 + .../EventHandlers/LocalizableAttribute.cs | 2 +- .../Expression/ExpressionRewriter.cs | 110 +- src/ReactiveUI/Expression/Reflection.cs | 266 +- .../Helpers/ArgumentExceptionHelper.cs | 205 +- src/ReactiveUI/Interactions/IInteraction.cs | 2 +- .../Interactions/IInteractionContext.cs | 2 +- src/ReactiveUI/Interactions/IOutputContext.cs | 2 +- src/ReactiveUI/Interactions/Interaction.cs | 82 +- .../Interactions/InteractionContext.cs | 9 +- .../UnhandledInteractionException.cs | 3 +- src/ReactiveUI/Interfaces/IActivatableView.cs | 6 +- .../Interfaces/IActivatableViewModel.cs | 2 +- src/ReactiveUI/Interfaces/ICanActivate.cs | 4 +- .../Interfaces/ICanForceManualActivation.cs | 6 +- .../Interfaces/ICreatesCommandBinding.cs | 41 +- .../ICreatesCustomizedCommandRebinding.cs | 2 +- .../ICreatesObservableForProperty.cs | 82 +- .../Interfaces/IHandleObservableErrors.cs | 2 +- src/ReactiveUI/Interfaces/IMessageBus.cs | 99 +- src/ReactiveUI/Interfaces/IObservedChange.cs | 4 +- .../Interfaces/IPlatformOperations.cs | 2 +- .../Interfaces/IPropertyBindingHook.cs | 2 +- .../IReactiveNotifyPropertyChanged.cs | 2 +- .../IReactivePropertyChangedEventArgs.cs | 2 +- src/ReactiveUI/Interfaces/IRegistrar.cs | 40 +- .../Interfaces/IRoutableViewModel.cs | 6 +- src/ReactiveUI/Interfaces/IScreen.cs | 2 +- .../Interfaces/ISuspensionDriver.cs | 59 +- src/ReactiveUI/Interfaces/ISuspensionHost.cs | 15 +- .../Interfaces/ISuspensionHost{TAppState}.cs | 2 +- src/ReactiveUI/Interfaces/IViewFor.cs | 6 +- src/ReactiveUI/Interfaces/IViewLocator.cs | 43 +- src/ReactiveUI/Interfaces/IViewModule.cs | 2 +- .../Interfaces/IWantsToRegisterStuff.cs | 2 +- src/ReactiveUI/Interfaces/ObservedChange.cs | 7 +- .../ReactivePropertyChangedEventArgs.cs | 7 +- .../ReactivePropertyChangingEventArgs.cs | 7 +- src/ReactiveUI/Internal/Broadcaster.cs | 157 + src/ReactiveUI/Internal/DelegateObserver.cs | 28 + .../Internal/Disposables/ActionDisposable.cs | 25 + .../Internal/Disposables/DisposableBag.cs | 174 + .../Disposables/DisposableSlotHelper.cs | 94 + .../Internal/Disposables/EmptyDisposable.cs | 29 + .../Internal/Disposables/MutableDisposable.cs | 33 + .../Internal/Disposables/OnceDisposable.cs | 72 + .../Internal/Disposables/SwapDisposable.cs | 33 + .../Internal/EventPatternObservable.cs | 52 + .../ExpressionChainParameters{TSender}.cs | 31 + .../Internal/ExpressionChainSink.cs | 278 + .../Internal/InteractionHandleObservable.cs | 190 + src/ReactiveUI/Internal/NeverObservable.cs | 28 + .../Internal/ObservableForPropertySink.cs | 138 + .../Internal/ObservedChangeValueSelector.cs | 56 + .../Internal/SingleValueObservable.cs | 27 + .../Internal/SingleValueObservable{T}.cs | 24 + src/ReactiveUI/Internal/StartObservable.cs | 41 + .../Subjects/BehaviorBroadcastSubject.cs | 135 + .../Internal/Subjects/BroadcastSubject.cs | 135 + .../Subjects/DelayableNotificationSubject.cs | 162 + .../Subjects/ReplayBroadcastSubject.cs | 145 + .../Internal/Subjects/SchedulingObserver.cs | 47 + .../Internal/SyncExecuteObservable.cs | 33 + src/ReactiveUI/Internal/TaskObservable.cs | 44 + src/ReactiveUI/Internal/TaskUnitObservable.cs | 46 + src/ReactiveUI/Internal/ThrowObservable.cs | 22 + src/ReactiveUI/Internal/ToUnitObservable.cs | 38 + .../Internal/WhenAny/EmptyObservable.cs | 34 + .../WhenAny/WhenAnyChangeSink.Arity1.cs | 57 + .../WhenAny/WhenAnyChangeSink.Arity10.cs | 396 + .../WhenAny/WhenAnyChangeSink.Arity11.cs | 426 + .../WhenAny/WhenAnyChangeSink.Arity12.cs | 455 + .../WhenAny/WhenAnyChangeSink.Arity2.cs | 160 + .../WhenAny/WhenAnyChangeSink.Arity3.cs | 188 + .../WhenAny/WhenAnyChangeSink.Arity4.cs | 216 + .../WhenAny/WhenAnyChangeSink.Arity5.cs | 244 + .../WhenAny/WhenAnyChangeSink.Arity6.cs | 272 + .../WhenAny/WhenAnyChangeSink.Arity7.cs | 308 + .../WhenAny/WhenAnyChangeSink.Arity8.cs | 338 + .../WhenAny/WhenAnyChangeSink.Arity9.cs | 367 + .../WhenAny/WhenAnyObservableMergeSink.cs | 167 + .../WhenAny/WhenAnyObservableSwitchSink.cs | 156 + .../WhenAny/WhenAnyValueSink.Arity1.cs | 58 + .../WhenAny/WhenAnyValueSink.Arity10.cs | 398 + .../WhenAny/WhenAnyValueSink.Arity11.cs | 428 + .../WhenAny/WhenAnyValueSink.Arity12.cs | 457 + .../WhenAny/WhenAnyValueSink.Arity2.cs | 162 + .../WhenAny/WhenAnyValueSink.Arity3.cs | 190 + .../WhenAny/WhenAnyValueSink.Arity4.cs | 222 + .../WhenAny/WhenAnyValueSink.Arity5.cs | 251 + .../WhenAny/WhenAnyValueSink.Arity6.cs | 280 + .../WhenAny/WhenAnyValueSink.Arity7.cs | 310 + .../WhenAny/WhenAnyValueSink.Arity8.cs | 340 + .../WhenAny/WhenAnyValueSink.Arity9.cs | 369 + src/ReactiveUI/Mixins/AutoPersistHelper.cs | 902 +- src/ReactiveUI/Mixins/BuilderMixins.cs | 851 +- src/ReactiveUI/Mixins/ChangeSetMixin.cs | 67 +- src/ReactiveUI/Mixins/CompatMixins.cs | 2 +- .../Mixins/DependencyResolverMixins.cs | 84 +- src/ReactiveUI/Mixins/ExpressionMixins.cs | 61 +- .../MutableDependencyResolverAOTExtensions.cs | 21 +- .../MutableDependencyResolverExtensions.cs | 52 +- .../Mixins/ObservableLoggingMixin.cs | 288 +- src/ReactiveUI/Mixins/ObservableMixins.cs | 261 +- src/ReactiveUI/Mixins/ObservedChangedMixin.cs | 97 +- src/ReactiveUI/Mixins/PreserveAttribute.cs | 2 +- .../ReactiveNotifyPropertyChangedMixin.cs | 216 +- src/ReactiveUI/Mixins/SwitchSubscribeMixin.cs | 415 +- src/ReactiveUI/Observable.cs | 12 +- .../INPCObservableForProperty.cs | 250 +- .../IROObservableForProperty.cs | 118 +- .../OAPHCreationHelperMixin.cs | 717 +- .../ObservableAsPropertyHelper.cs | 583 +- .../POCOObservableForProperty.cs | 75 +- src/ReactiveUI/ObservableFuncMixins.cs | 129 +- src/ReactiveUI/Observables.cs | 36 - .../android/AndroidCommandBinders.cs | 20 +- .../android/AndroidObservableForWidgets.cs | 218 +- .../Platforms/android/AutoSuspendHelper.cs | 143 +- .../android/BundleSuspensionDriver.cs | 98 +- .../Platforms/android/ContextExtensions.cs | 120 +- .../Platforms/android/ControlFetcherMixin.cs | 327 +- .../android/FlexibleCommandBinder.cs | 307 +- .../Platforms/android/HandlerScheduler.cs | 69 +- .../Platforms/android/ILayoutViewHost.cs | 2 +- .../android/IgnoreResourceAttribute.cs | 2 +- .../Platforms/android/JavaHolder.cs | 10 +- .../Platforms/android/LayoutViewHost.cs | 37 +- .../Platforms/android/LinkerOverrides.cs | 35 +- .../Platforms/android/ObjectExtension.cs | 9 +- .../Platforms/android/PlatformOperations.cs | 4 +- .../android/PlatformRegistrations.cs | 6 +- .../Platforms/android/ReactiveActivity.cs | 140 +- .../android/ReactiveActivity{TViewModel}.cs | 5 +- .../Platforms/android/ReactiveFragment.cs | 25 +- .../android/ReactiveFragment{TViewModel}.cs | 6 +- .../Platforms/android/ReactiveViewHost.cs | 72 +- .../Platforms/android/ResolveStrategy.cs | 2 +- .../android/SharedPreferencesExtensions.cs | 36 +- .../Platforms/android/UsbManagerExtensions.cs | 98 +- .../android/ViewCommandExtensions.cs | 15 +- .../Platforms/android/ViewMixins.cs | 15 +- .../android/WireUpResourceAttribute.cs | 6 +- .../AppSupportJsonSuspensionDriver.cs | 14 +- .../apple-common/BlockObserveValueDelegate.cs | 2 +- .../DateTimeOffsetToNSDateConverter.cs | 1 - .../Converters/DateTimeToNSDateConverter.cs | 1 - .../Converters/NSDateToDateTimeConverter.cs | 1 - .../NSDateToDateTimeOffsetConverter.cs | 1 - .../NSDateToNullableDateTimeConverter.cs | 1 - ...NSDateToNullableDateTimeOffsetConverter.cs | 1 - ...NullableDateTimeOffsetToNSDateConverter.cs | 1 - .../NullableDateTimeToNSDateConverter.cs | 1 - .../Platforms/apple-common/IndexNormalizer.cs | 2 +- .../apple-common/KVOObservableForProperty.cs | 48 +- .../apple-common/NSRunloopScheduler.cs | 2 +- .../apple-common/ObservableForPropertyBase.cs | 49 +- .../apple-common/PlatformOperations.cs | 2 +- .../Platforms/apple-common/ReactiveControl.cs | 2 +- .../apple-common/ReactiveImageView.cs | 3 +- .../ReactiveSplitViewController.cs | 3 +- .../Platforms/apple-common/ReactiveView.cs | 2 +- .../apple-common/ReactiveViewController.cs | 2 +- .../apple-common/TargetActionCommandBinder.cs | 5 +- .../apple-common/UIViewControllerMixins.cs | 2 +- .../Platforms/apple-common/Update.cs | 2 +- .../Platforms/apple-common/UpdateType.cs | 2 +- .../apple-common/ViewModelViewHost.cs | 2 +- .../Platforms/ios/LinkerOverrides.cs | 2 +- .../mac/AppKitObservableForProperty.cs | 2 +- .../Platforms/mac/AutoSuspendHelper.cs | 4 +- .../Platforms/mac/ReactiveWindowController.cs | 2 +- .../ComponentModelFallbackConverter.cs | 95 +- .../net/ComponentModelFallbackConverter.cs | 63 +- .../Platforms/net/PlatformRegistrations.cs | 13 +- .../Platforms/tizen/EcoreMainloopScheduler.cs | 2 +- .../Platforms/tizen/PlatformOperations.cs | 2 +- .../Platforms/tizen/PlatformRegistrations.cs | 2 +- .../Platforms/tvos/LinkerOverrides.cs | 2 +- .../tvos/UIKitObservableForProperty.cs | 3 +- .../CollectionViewSectionInformation.cs | 2 +- .../uikit-common/CommonReactiveSource.cs | 6 +- .../uikit-common/FlexibleCommandBinder.cs | 18 +- .../uikit-common/ISectionInformation.cs | 2 +- .../uikit-common/IUICollViewAdapter.cs | 2 +- .../ReactiveCollectionReusableView.cs | 2 +- .../uikit-common/ReactiveCollectionView.cs | 2 +- .../ReactiveCollectionViewCell.cs | 2 +- .../ReactiveCollectionViewController.cs | 2 +- .../ReactiveCollectionViewSource.cs | 2 +- .../ReactiveCollectionViewSourceExtensions.cs | 2 +- .../ReactiveNavigationController.cs | 2 +- .../ReactivePageViewController.cs | 2 +- .../uikit-common/ReactiveTabBarController.cs | 2 +- .../uikit-common/ReactiveTableView.cs | 2 +- .../uikit-common/ReactiveTableViewCell.cs | 2 +- .../ReactiveTableViewController.cs | 2 +- .../uikit-common/ReactiveTableViewSource.cs | 2 +- .../ReactiveTableViewSourceExtensions.cs | 2 +- .../Platforms/uikit-common/RoutedViewHost.cs | 4 +- .../uikit-common/SectionInfoIdGenerator.cs | 2 +- .../uikit-common/TableSectionHeader.cs | 2 +- .../uikit-common/TableSectionInformation.cs | 2 +- .../uikit-common/UICollectionViewAdapter.cs | 2 +- .../uikit-common/UITableViewAdapter.cs | 2 +- .../CallerArgumentExpressionAttribute.cs | 6 +- .../Polyfills/DoesNotReturnIfAttribute.cs | 7 +- .../DynamicallyAccessedMemberTypes.cs | 15 +- .../DynamicallyAccessedMembersAttribute.cs | 7 +- src/ReactiveUI/Polyfills/HashCode.cs | 131 + src/ReactiveUI/Polyfills/Index.cs | 93 + src/ReactiveUI/Polyfills/IsExternalInit.cs | 6 +- .../Polyfills/MaybeNullWhenAttribute.cs | 6 +- .../Polyfills/MemberNotNullAttribute.cs | 9 +- src/ReactiveUI/Polyfills/NotNullAttribute.cs | 5 +- .../Polyfills/NotNullWhenAttribute.cs | 8 +- src/ReactiveUI/Polyfills/Range.cs | 85 + .../Polyfills/RequiresDynamicCodeAttribute.cs | 8 +- .../RequiresUnreferencedCodeAttribute.cs | 7 +- .../UnconditionalSuppressMessageAttribute.cs | 7 +- .../CombinedReactiveCommand.cs | 568 +- .../ReactiveCommand/IReactiveCommand.cs | 2 +- .../ReactiveCommand/ReactiveCommand.cs | 1285 +- .../ReactiveCommand/ReactiveCommandBase.cs | 39 +- .../ReactiveCommand/ReactiveCommandMixins.cs | 402 +- .../ReactiveCommand{TParam,TResult}.cs | 589 + .../ReactiveObject/IReactiveObject.cs | 4 +- .../IReactiveObjectExtensions.cs | 306 +- .../ReactiveObject/ReactiveObject.cs | 39 +- .../ReactiveObject/ReactiveRecord.cs | 55 +- .../ReactiveProperty/IReactiveProperty.cs | 4 +- .../ReactiveProperty/ReactiveProperty.cs | 652 +- .../ReactivePropertyMixins.cs | 67 +- .../SingletonDataErrorsChangedEventArgs.cs | 4 +- .../SingletonPropertyChangedEventArgs.cs | 4 +- src/ReactiveUI/ReactiveUI.csproj | 142 +- .../DependencyResolverRegistrar.cs | 32 +- src/ReactiveUI/Registration/Registrations.cs | 83 +- src/ReactiveUI/Routing/MessageBus.cs | 194 +- src/ReactiveUI/Routing/NotAWeakReference.cs | 8 +- .../Routing/RoutableViewModelMixin.cs | 240 +- src/ReactiveUI/Routing/RoutingState.cs | 172 +- src/ReactiveUI/Routing/RoutingStateMixins.cs | 6 +- src/ReactiveUI/RxCacheSize.cs | 43 +- src/ReactiveUI/RxSchedulers.cs | 22 +- src/ReactiveUI/RxState.cs | 53 +- src/ReactiveUI/RxSuspension.cs | 23 +- src/ReactiveUI/Scheduler/ScheduledSubject.cs | 113 +- .../Scheduler/WaitForDispatcherScheduler.cs | 28 +- .../Subjects/IReactiveSubject{T}.cs | 14 + .../Suspension/DummySuspensionDriver.cs | 25 +- src/ReactiveUI/Suspension/SuspensionHost.cs | 86 +- .../Suspension/SuspensionHostExtensions.cs | 453 +- .../Suspension/SuspensionHost{TAppState}.cs | 54 +- src/ReactiveUI/Temp/CompositeDisposable.cs | 585 + src/ReactiveUI/Temp/DisposableMixins.cs | 30 + src/ReactiveUI/Temp/ICancelable.cs | 20 + src/ReactiveUI/Temp/IScheduler.cs | 47 + src/ReactiveUI/Temp/ISubject.cs | 26 + src/ReactiveUI/Temp/Schedulers.cs | 134 + src/ReactiveUI/Temp/Unit.cs | 62 + src/ReactiveUI/UnhandledErrorException.cs | 2 +- src/ReactiveUI/VariadicTemplates.cs | 5287 ----- src/ReactiveUI/VariadicTemplates.tt | 542 - src/ReactiveUI/View/DefaultViewLocator.cs | 190 +- .../ExcludeFromViewRegistrationAttribute.cs | 2 +- .../View/SingleInstanceViewAttribute.cs | 2 +- src/ReactiveUI/View/ViewContractAttribute.cs | 2 +- src/ReactiveUI/View/ViewLocator.cs | 5 +- .../View/ViewLocatorNotFoundException.cs | 2 +- src/ReactiveUI/View/ViewMappingBuilder.cs | 73 +- .../WhenAny/ObservableExtensions.cs | 21 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity1.cs | 273 + .../WhenAny/WhenAnyMixin.Arity10.cs | 704 + .../WhenAny/WhenAnyMixin.Arity11.cs | 767 + .../WhenAny/WhenAnyMixin.Arity12.cs | 832 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity2.cs | 337 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity3.cs | 395 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity4.cs | 453 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity5.cs | 563 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity6.cs | 657 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity7.cs | 727 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity8.cs | 600 + src/ReactiveUI/WhenAny/WhenAnyMixin.Arity9.cs | 652 + .../WhenAny/WhenAnyObservableMixin.Arity1.cs | 29 + .../WhenAny/WhenAnyObservableMixin.Arity10.cs | 142 + .../WhenAny/WhenAnyObservableMixin.Arity11.cs | 151 + .../WhenAny/WhenAnyObservableMixin.Arity12.cs | 160 + .../WhenAny/WhenAnyObservableMixin.Arity2.cs | 66 + .../WhenAny/WhenAnyObservableMixin.Arity3.cs | 75 + .../WhenAny/WhenAnyObservableMixin.Arity4.cs | 84 + .../WhenAny/WhenAnyObservableMixin.Arity5.cs | 93 + .../WhenAny/WhenAnyObservableMixin.Arity6.cs | 106 + .../WhenAny/WhenAnyObservableMixin.Arity7.cs | 115 + .../WhenAny/WhenAnyObservableMixin.Arity8.cs | 124 + .../WhenAny/WhenAnyObservableMixin.Arity9.cs | 133 + .../Components/App.razor | 24 +- .../Components/Layout/MainLayout.razor | 4 +- .../Components/Layout/MainLayout.razor.css | 36 +- .../Components/Layout/NavMenu.razor | 2 +- .../Components/Layout/NavMenu.razor.css | 40 +- .../Layout/ReactiveRouterHost.razor | 5 +- .../Components/Layout/ReconnectModal.razor | 4 +- .../Layout/ReconnectModal.razor.css | 41 +- .../Components/Pages/Counter.razor | 1 + .../Components/Pages/Error.razor | 14 +- .../Components/Pages/Weather.razor | 33 +- .../Components/Routes.razor | 4 +- .../Models/ChatMessage.cs | 4 +- .../Models/ChatNetworkMessage.cs | 2 +- .../Models/ChatRoom.cs | 5 +- .../Models/ChatState.cs | 5 +- .../Models/ChatStateChanged.cs | 5 +- .../Models/RoomEventMessage.cs | 5 +- .../Program.cs | 14 +- .../Properties/launchSettings.json | 38 +- .../ReactiveUI.Builder.BlazorServer.csproj | 4 +- .../Services/AppInstance.cs | 13 +- .../Services/AppLifetimeCoordinator.cs | 53 +- .../Services/FileJsonSuspensionDriver.cs | 79 +- .../Services/ReactiveUiAppHostedService.cs | 21 +- .../Services/RoomEventKind.cs | 4 +- .../ViewModels/AppBootstrapper.cs | 4 +- .../ViewModels/ChatRoomViewModel.cs | 55 +- .../ViewModels/LobbyViewModel.cs | 145 +- .../Views/ChatRoomView.razor | 2 +- .../Views/ChatRoomView.razor.cs | 45 +- .../Views/LobbyView.razor | 4 +- .../Views/LobbyView.razor.cs | 90 +- .../appsettings.Development.json | 10 +- .../appsettings.json | 14 +- .../wwwroot/app.css | 10 +- .../lib/bootstrap/dist/css/bootstrap-grid.css | 7872 ++++--- .../bootstrap/dist/css/bootstrap-grid.rtl.css | 7873 ++++--- .../bootstrap/dist/css/bootstrap-reboot.css | 668 +- .../dist/css/bootstrap-reboot.rtl.css | 672 +- .../dist/css/bootstrap-utilities.css | 8586 +++---- .../dist/css/bootstrap-utilities.rtl.css | 8584 +++---- .../lib/bootstrap/dist/css/bootstrap.css | 18412 ++++++++------- .../lib/bootstrap/dist/css/bootstrap.rtl.css | 18415 +++++++++------- .../lib/bootstrap/dist/js/bootstrap.bundle.js | 12146 +++++----- .../lib/bootstrap/dist/js/bootstrap.esm.js | 7049 +++--- .../lib/bootstrap/dist/js/bootstrap.js | 8848 ++++---- .../ReactiveUI.Builder.WpfApp/App.xaml | 5 +- .../ReactiveUI.Builder.WpfApp/App.xaml.cs | 57 +- .../ReactiveUI.Builder.WpfApp/AssemblyInfo.cs | 2 +- .../ReactiveUI.Builder.WpfApp/MainWindow.xaml | 3 +- .../MainWindow.xaml.cs | 19 +- .../Models/ChatMessage.cs | 4 +- .../Models/ChatNetworkMessage.cs | 2 +- .../Models/ChatRoom.cs | 7 +- .../Models/ChatState.cs | 5 +- .../Models/ChatStateChanged.cs | 5 +- .../Models/RoomEventMessage.cs | 4 +- .../ReactiveUI.Builder.WpfApp.csproj | 22 +- .../Services/AppInstance.cs | 12 +- .../Services/AppLifetimeCoordinator.cs | 36 +- .../Services/ChatNetworkService.cs | 181 +- .../Services/FileJsonSuspensionDriver.cs | 80 +- .../Services/RoomEventKind.cs | 4 +- .../ViewModels/AppBootstrapper.cs | 4 +- .../ViewModels/ChatRoomViewModel.cs | 45 +- .../ViewModels/LobbyViewModel.cs | 151 +- .../Views/ChatRoomView.xaml.cs | 14 +- .../Views/LobbyView.xaml | 1 - .../Views/LobbyView.xaml.cs | 20 +- src/examples/ReactiveUI.Samples.Maui/App.xaml | 6 +- .../ReactiveUI.Samples.Maui/App.xaml.cs | 4 +- .../ReactiveUI.Samples.Maui/AppShell.xaml | 3 +- .../ReactiveUI.Samples.Maui/AppShell.xaml.cs | 2 +- .../ReactiveUI.Samples.Maui/LoginPage.xaml | 6 +- .../ReactiveUI.Samples.Maui/LoginPage.xaml.cs | 6 +- .../ReactiveUI.Samples.Maui/LoginViewModel.cs | 12 +- .../ReactiveUI.Samples.Maui/MauiProgram.cs | 2 +- .../ReactiveUI.Samples.Maui.csproj | 20 +- .../ReactiveUI.Samples.Winforms/LoginView.cs | 39 +- .../LoginViewModel.cs | 12 +- .../ReactiveUI.Samples.Winforms/MainForm.cs | 14 +- .../ReactiveUI.Samples.Winforms/Program.cs | 2 +- .../ReactiveUI.Samples.Winforms.csproj | 14 +- .../ReactiveUI.Samples.Wpf/App.xaml.cs | 2 +- .../ReactiveUI.Samples.Wpf/AssemblyInfo.cs | 2 +- .../ReactiveUI.Samples.Wpf/LoginView.xaml.cs | 6 +- .../ReactiveUI.Samples.Wpf/LoginViewModel.cs | 12 +- .../ReactiveUI.Samples.Wpf/MainWindow.xaml.cs | 2 +- .../ReactiveUI.Samples.Wpf.csproj | 14 +- src/reactiveui.slnx | 107 +- src/stylecop.json | 8 +- .../AOTCompatibilityTests.cs | 28 +- .../ReactiveUI.AOTTests/AdvancedAOTTests.cs | 9 +- .../ReactiveUI.AOTTests/AssemblyHooks.cs | 14 +- .../ComprehensiveAOTMarkupTests.cs | 54 +- .../ComprehensiveAOTTests.cs | 37 +- .../FinalAOTValidationTests.cs | 38 +- .../ReactiveUI.AOT.Tests.csproj | 23 +- .../StringBasedObservationTests.cs | 102 +- .../StringBasedSemanticsTests.cs | 54 +- .../TestActivatableViewModel.cs | 4 +- .../ReactiveUI.AOTTests/TestReactiveObject.cs | 14 +- .../TestRoutableViewModel.cs | 7 +- .../ViewLocatorAOTMappingTests.cs | 58 +- .../BlazorReactiveUIBuilderExtensionsTests.cs | 240 +- .../PlatformOperationsTests.cs | 4 +- .../ReactiveComponentBaseTests.cs | 27 +- .../ReactiveInjectableComponentBaseTests.cs | 40 +- .../ReactiveLayoutComponentBaseTests.cs | 21 +- .../ReactiveOwningComponentBaseTests.cs | 21 +- .../ReactiveUI.Blazor.Tests.csproj | 20 +- .../ActivationForViewFetcherTests.cs | 13 +- .../AssemblyHooks.cs | 6 +- .../AssemblyInfo.Parallel.cs | 5 +- .../ReactiveUI.Builder.Maui.Tests.csproj | 34 +- .../ReactiveUIBuilderMauiTests.cs | 10 +- .../TestDispatcher.cs | 10 +- .../TestDispatcherProvider.cs | 4 +- .../AssemblyHooks.Parallel.cs | 3 +- .../ReactiveUI.Builder.Tests/AssemblyHooks.cs | 15 +- .../Executors/BuilderTestExecutor.cs | 6 +- .../Executors/BuilderTestExecutorBase.cs | 5 +- ...rInstanceMixinsHappyPathTests.HighArity.cs | 779 + .../BuilderInstanceMixinsHappyPathTests.cs | 1213 +- .../BuilderInstanceMixinsNullActionTests.cs | 359 +- .../BuilderInstanceMixinsNullInstanceTests.cs | 467 +- .../BuilderInstanceMixinsTests.Arity01to06.cs | 834 + .../BuilderInstanceMixinsTests.Arity07to09.cs | 650 + .../BuilderInstanceMixinsTests.Arity10to12.cs | 624 + .../BuilderInstanceMixinsTests.Arity13to14.cs | 472 + .../BuilderInstanceMixinsTests.Arity15to16.cs | 510 + ...uilderInstanceMixinsTests.HelpersAssert.cs | 608 + ...uilderInstanceMixinsTests.HelpersInvoke.cs | 748 + .../Mixins/BuilderInstanceMixinsTests.cs | 3557 +-- .../Mixins/BuilderMixinsTests.cs | 359 +- .../Mixins/BuilderSchedulerMixinsTests.cs | 32 +- .../Blazor/ReactiveUIBuilderBlazorTests.cs | 23 +- .../Drawing/ReactiveUIBuilderDrawingTests.cs | 22 +- .../ReactiveUI.Builder.Tests.csproj | 42 +- .../ReactiveUIBuilderBlockingTests.cs | 9 +- .../ReactiveUIBuilderConverterTests.cs | 87 +- .../ReactiveUIBuilderCoreTests.cs | 85 +- .../ReactiveUIBuilderRxAppMigrationTests.cs | 167 +- .../TestConfiguration.cs | 7 - .../ActivationForViewFetcherTest.cs | 40 +- .../ActivationForViewFetcherTests.cs | 43 +- .../ReactiveUI.Maui.Tests/AssemblyHooks.cs | 16 +- .../AssemblyInfo.Parallel.cs | 5 +- .../AutoSuspendHelperTest.cs | 8 +- .../BooleanToVisibilityTypeConverterTest.cs | 15 +- .../Builder/MauiDispatcherSchedulerTest.cs | 27 +- .../MauiReactiveUIBuilderExtensionsTest.cs | 38 +- .../Internal/MauiReactiveHelpersTest.cs | 37 +- .../MauiReactiveUIBuilderExtensionsTest.cs | 62 +- .../ReactiveUI.Maui.Tests/MauiTestExecutor.cs | 11 +- .../PlatformOperationsTest.cs | 2 +- .../ReactiveCarouselViewTests.cs | 9 +- .../ReactiveContentPageTests.cs | 9 +- .../ReactiveContentViewTests.cs | 9 +- .../ReactiveFlyoutPageTests.cs | 9 +- .../ReactiveImageItemViewTests.cs | 16 +- .../ReactiveMasterDetailPageTests.cs | 9 +- .../ReactiveMultiPageTests.cs | 13 +- .../ReactiveNavigationPageTests.cs | 9 +- .../ReactiveUI.Maui.Tests/ReactivePageTest.cs | 14 +- .../ReactiveShellContentTest.cs | 21 +- .../ReactiveShellTests.cs | 9 +- .../ReactiveTabbedPageTests.cs | 9 +- .../ReactiveTextItemViewTests.cs | 12 +- .../ReactiveUI.Maui.Tests.csproj | 36 +- .../RoutedViewHostGenericTests.cs | 81 +- .../RoutedViewHostTest.cs | 70 +- .../ReactiveUI.Maui.Tests/TestDispatcher.cs | 10 +- .../TestDispatcherProvider.cs | 4 +- .../ViewModelViewHostGenericTests.cs | 81 +- .../ViewModelViewHostTest.cs | 146 +- .../Platforms/android/MainActivity.cs | 2 +- .../android/PropertyBindingTestViews.cs | 14 + .../Platforms/cocoa/AppDelegate.cs | 8 + .../Platforms/cocoa/IndexNormalizerTest.cs | 10 + .../Platforms/cocoa/KVOBindingTests.cs | 3 + .../cocoa/PropertyBindingTestViews.cs | 69 +- .../Platforms/cocoa/UnitTestAppDelegate.cs | 26 +- .../ReactiveUI.Splat.Tests/AssemblyHooks.cs | 12 +- .../ReactiveUI.Splat.Tests.csproj | 18 +- .../SplatAdapterTests.cs | 40 +- .../ApiExtensions.cs | 4 +- .../AppBuilder/AppBuilderTestExecutor.cs | 2 +- .../AppBuilder/AppBuilderTestHelper.cs | 9 +- .../AppBuilder/BaseAppBuilderTestExecutor.cs | 5 +- .../WithSchedulerAndMessageBusExecutor.cs | 5 +- .../Logging/LoggingRegistrationExecutor.cs | 2 +- .../LoggingRegistrationExecutorExtensions.cs | 2 +- .../Logging/TestLogManager.cs | 2 +- .../Logging/TestLogger.cs | 2 +- .../MessageBusTestContextExtensions.cs | 2 +- .../MessageBus/WithMessageBusExecutor.cs | 5 +- .../Schedulers/TestContextExtensions.cs | 5 +- .../Schedulers/VirtualTimeScheduler.cs | 26 +- .../Schedulers/WithSchedulerExecutor.cs | 2 +- .../WithVirtualTimeSchedulerExecutor.cs | 2 +- .../Sequencing/TestSequencer.cs | 46 +- .../SuspensionHostTestExecutor.cs | 9 +- .../Mocks/RaceConditionFixture.cs | 20 +- .../Mocks/RaceConditionNameOfFixture.cs | 15 +- .../CommonGuiMocks/Mocks/TestScreen.cs | 8 +- .../CommonGuiMocks/ProductionMode.cs | 5 +- .../ReactiveUI.TestGuiMocks.csproj | 37 +- ...rovalTests.Testing.DotNet10_0.verified.txt | 2 +- ...provalTests.Testing.DotNet8_0.verified.txt | 2 +- ...provalTests.Testing.DotNet9_0.verified.txt | 2 +- .../API/ApiApprovalTests.cs | 4 +- .../AppBuilderTestBaseTests.cs | 39 +- .../ReactiveUI.Testing.Tests/AssemblyHooks.cs | 13 +- .../MessageBusExtensionsTests.cs | 22 +- .../ReactiveUI.Testing.Tests.csproj | 30 +- .../ReactiveUI.Testing.Tests/RxTestTests.cs | 58 +- .../SchedulerExtensionTests.cs | 58 +- .../TestConfiguration.cs | 8 - .../ReactiveUI.Testing.Tests/TestFixture.cs | 6 +- .../TestFixtureBuilder.cs | 35 +- .../TestFixtureBuilderExtensionTests.cs | 38 +- .../TestSequencerTests.cs | 35 +- ...alTests.ReactiveUI.DotNet10_0.verified.txt | 2 +- ...valTests.ReactiveUI.DotNet6_0.verified.txt | 2 +- ...valTests.ReactiveUI.DotNet7_0.verified.txt | 2 +- ...valTests.ReactiveUI.DotNet8_0.verified.txt | 2 +- ...valTests.ReactiveUI.DotNet9_0.verified.txt | 2 +- ...provalTests.ReactiveUI.Net4_7.verified.txt | 2 +- ...rovalTests.Testing.DotNet10_0.verified.txt | 2 +- ...provalTests.Testing.DotNet6_0.verified.txt | 2 +- ...provalTests.Testing.DotNet7_0.verified.txt | 2 +- ...provalTests.Testing.DotNet8_0.verified.txt | 2 +- ...provalTests.Testing.DotNet9_0.verified.txt | 2 +- ...iApprovalTests.Testing.Net4_7.verified.txt | 2 +- .../ReactiveUI.Tests/API/ApiApprovalTests.cs | 4 +- .../Activation/ActivatingView.cs | 2 +- .../Activation/ActivatingViewFetcher.cs | 21 +- .../Activation/ActivatingViewModel.cs | 4 +- .../Activation/ActivatingViewModelTests.cs | 5 +- .../Activation/ActivatingViewTests.cs | 35 +- .../Activation/CanActivateViewFetcherTests.cs | 102 +- .../Activation/DerivedActivatingViewModel.cs | 2 +- .../Activation/ViewModelActivatorTests.cs | 23 +- src/tests/ReactiveUI.Tests/AssemblyHooks.cs | 10 +- .../AutoPersist/AutoPersistCollectionTest.cs | 96 +- .../AutoPersist/AutoPersistHelperTest.cs | 85 +- .../BindingTypeConvertersUnitTests.cs | 107 +- .../Bindings/BindingTypeConverterTests.cs | 36 +- ...sCommandBindingViaCommandParameterTests.cs | 103 +- .../CreatesCommandBindingViaEventTests.cs | 211 +- .../Converters/ConverterAffinityTests.cs | 124 +- .../ConverterMigrationHelperTests.cs | 181 +- .../Converters/ConverterRegistryTests.cs | 97 +- .../ConverterServiceIntegrationTests.cs | 39 +- .../PlatformConverterAffinityTests.cs | 5 +- .../Mocks/MockBindingConverterResolver.cs | 19 +- .../Mocks/MockBindingHookEvaluator.cs | 9 +- .../MockPropertyBindingExpressionCompiler.cs | 46 +- .../Unit/BindingConverterResolverTests.cs | 55 +- .../Unit/BindingHookEvaluatorTests.cs | 47 +- .../PropertyBindingExpressionCompilerTests.cs | 135 +- .../PropertyBindingMixinsTests.cs | 202 +- .../BooleanToStringTypeConverterTests.cs | 16 +- .../ByteToNullableByteTypeConverterTests.cs | 48 +- .../ByteToStringTypeConverterTests.cs | 44 +- .../DateOnlyToStringTypeConverterTests.cs | 20 +- ...ateTimeOffsetToStringTypeConverterTests.cs | 20 +- .../DateTimeToStringTypeConverterTests.cs | 42 +- ...imalToNullableDecimalTypeConverterTests.cs | 48 +- .../DecimalToStringTypeConverterTests.cs | 106 +- ...oubleToNullableDoubleTypeConverterTests.cs | 26 +- .../DoubleToStringTypeConverterTests.cs | 102 +- .../EqualityTypeConverterTests.cs | 88 +- .../GuidToStringTypeConverterTests.cs | 16 +- ...egerToNullableIntegerTypeConverterTests.cs | 48 +- .../IntegerToStringTypeConverterTests.cs | 83 +- .../LongToNullableLongTypeConverterTests.cs | 48 +- .../LongToStringTypeConverterTests.cs | 44 +- ...llableBooleanToStringTypeConverterTests.cs | 20 +- .../NullableByteToByteTypeConverterTests.cs | 52 +- .../NullableByteToStringTypeConverterTests.cs | 40 +- ...lableDateOnlyToStringTypeConverterTests.cs | 16 +- ...ateTimeOffsetToStringTypeConverterTests.cs | 16 +- ...lableDateTimeToStringTypeConverterTests.cs | 26 +- ...lableDecimalToDecimalTypeConverterTests.cs | 52 +- ...llableDecimalToStringTypeConverterTests.cs | 60 +- ...ullableDoubleToDoubleTypeConverterTests.cs | 31 +- ...ullableDoubleToStringTypeConverterTests.cs | 60 +- .../NullableGuidToStringTypeConverterTests.cs | 16 +- ...lableIntegerToIntegerTypeConverterTests.cs | 52 +- ...llableIntegerToStringTypeConverterTests.cs | 42 +- .../NullableLongToLongTypeConverterTests.cs | 52 +- .../NullableLongToStringTypeConverterTests.cs | 42 +- .../NullableShortToShortTypeConverterTests.cs | 52 +- ...NullableShortToStringTypeConverterTests.cs | 42 +- ...ullableSingleToSingleTypeConverterTests.cs | 52 +- ...ullableSingleToStringTypeConverterTests.cs | 60 +- ...lableTimeOnlyToStringTypeConverterTests.cs | 16 +- ...lableTimeSpanToStringTypeConverterTests.cs | 16 +- .../ShortToNullableShortTypeConverterTests.cs | 48 +- .../ShortToStringTypeConverterTests.cs | 44 +- ...ingleToNullableSingleTypeConverterTests.cs | 48 +- .../SingleToStringTypeConverterTests.cs | 62 +- .../TypeConverters/StringConverterTests.cs | 60 +- .../StringToBooleanTypeConverterTests.cs | 42 +- .../StringToByteTypeConverterTests.cs | 80 +- .../StringToDateOnlyTypeConverterTests.cs | 30 +- ...tringToDateTimeOffsetTypeConverterTests.cs | 30 +- .../StringToDateTimeTypeConverterTests.cs | 40 +- .../StringToDecimalTypeConverterTests.cs | 68 +- .../StringToDoubleTypeConverterTests.cs | 75 +- .../StringToGuidTypeConverterTests.cs | 34 +- .../StringToIntegerTypeConverterTests.cs | 74 +- .../StringToLongTypeConverterTests.cs | 74 +- ...ringToNullableBooleanTypeConverterTests.cs | 38 +- .../StringToNullableByteTypeConverterTests.cs | 39 +- ...ingToNullableDateOnlyTypeConverterTests.cs | 26 +- ...ullableDateTimeOffsetTypeConverterTests.cs | 26 +- ...ingToNullableDateTimeTypeConverterTests.cs | 36 +- ...ringToNullableDecimalTypeConverterTests.cs | 27 +- ...tringToNullableDoubleTypeConverterTests.cs | 34 +- .../StringToNullableGuidTypeConverterTests.cs | 26 +- ...ringToNullableIntegerTypeConverterTests.cs | 33 +- .../StringToNullableLongTypeConverterTests.cs | 33 +- ...StringToNullableShortTypeConverterTests.cs | 33 +- ...tringToNullableSingleTypeConverterTests.cs | 28 +- ...ingToNullableTimeOnlyTypeConverterTests.cs | 26 +- ...ingToNullableTimeSpanTypeConverterTests.cs | 26 +- .../StringToShortTypeConverterTests.cs | 74 +- .../StringToSingleTypeConverterTests.cs | 79 +- .../StringToTimeOnlyTypeConverterTests.cs | 30 +- .../StringToTimeSpanTypeConverterTests.cs | 34 +- .../StringToUriTypeConverterTests.cs | 26 +- .../TimeOnlyToStringTypeConverterTests.cs | 20 +- .../TimeSpanToStringTypeConverterTests.cs | 20 +- .../UriToStringTypeConverterTests.cs | 24 +- .../ReactiveUI.Tests/ChainedComparerTest.cs | 16 +- .../ReactiveUI.Tests/ChangeSetMixinTest.cs | 31 +- .../CommandBinding/CommandBindingTests.cs | 95 +- .../Commands/CombinedReactiveCommandTest.cs | 49 +- .../Commands/CreatesCommandBindingTests.cs | 5 +- .../{ICommandHolder.cs => CommandHolder.cs} | 4 +- .../Commands/Mocks/FakeCommand.cs | 2 +- .../Commands/Mocks/ReactiveCommandHolder.cs | 2 +- .../ReactiveCommandTest.CreationTasks.cs | 454 + .../ReactiveCommandTest.ExecutionAndInvoke.cs | 781 + ...iveCommandTest.IsExecutingAndExceptions.cs | 489 + .../Commands/ReactiveCommandTest.cs | 1594 +- .../Comparers/OrderedComparerTests.cs | 66 +- .../ReactiveUI.Tests/Core/AttributeTests.cs | 8 +- .../Core/DataStructureTests.cs | 26 +- src/tests/ReactiveUI.Tests/Core/EnumTests.cs | 5 +- .../ReactiveUI.Tests/Core/ExceptionTests.cs | 44 +- .../Core/InternalUtilitiesTests.cs | 14 +- .../ReactiveUI.Tests/Core/ObservablesTests.cs | 2 +- .../ReactiveUI.Tests/Core/SingletonTests.cs | 2 +- .../Expression/CompiledPropertyChainTests.cs | 259 +- .../Expression/ExpressionMixinsTests.cs | 2 +- .../Expression/ExpressionRewriterTests.cs | 125 +- .../Expression/ReflectionAdvancedTests.cs | 202 +- .../Expression/ReflectionTests.cs | 285 +- .../IROObservableForPropertyTest.cs | 33 +- .../Infrastructure/RxAppTestExtensions.cs | 5 +- .../InteractionBinderImplementationTests.cs | 83 +- .../ReactiveUI.Tests/InteractionsTest.cs | 323 +- .../Locator/DefaultViewLocatorTests.cs | 136 +- .../Locator/ViewLocatorTests.cs | 15 +- .../Locator/ViewMappingBuilderTests.cs | 172 +- .../MessageBus/MessageBusTest.cs | 357 +- ...bleDependencyResolverAOTExtensionsTests.cs | 24 +- ...utableDependencyResolverExtensionsTests.cs | 100 +- .../Mixins/ObservableLoggingMixinTests.cs | 145 +- .../Mixins/SwitchSubscribeMixinTests.cs | 257 +- .../Mocks/AnotherViewModel.cs | 6 +- .../Mocks/CommandBindViewModel.cs | 16 +- .../Mocks/ExampleViewModel.cs | 6 +- .../Mocks/ExampleWindowViewModel.cs | 6 +- .../Mocks/FakeCollectionModel.cs | 5 +- .../Mocks/FakeCollectionViewModel.cs | 6 +- .../Mocks/FakeNestedViewModel.cs | 2 +- .../ReactiveUI.Tests/Mocks/FakeViewModel.cs | 2 +- src/tests/ReactiveUI.Tests/Mocks/Foo.cs | 30 +- .../ReactiveUI.Tests/Mocks/FooViewModel.cs | 20 +- .../Mocks/InteractionAncestorView.cs | 2 +- .../Mocks/InteractionAncestorViewModel.cs | 4 +- .../Mocks/InteractionBindView.cs | 2 +- .../Mocks/InteractionBindViewModel.cs | 4 +- .../Mocks/NeverUsedViewModel.cs | 6 +- .../Mocks/PropertyBindModel.cs | 2 +- .../Mocks/SingleInstanceExampleViewModel.cs | 6 +- .../Mocks/ViewModelWithWeirdName.cs | 6 +- .../Mocks/OAPHIndexerTestFixture.cs | 47 +- .../ObservableAsPropertyHelperTest.cs | 176 +- .../OAPHCreationHelperMixinTest.cs | 102 +- .../ReactiveUI.Tests/ObservableMixinsTest.cs | 31 +- .../ObservedChanged/Mocks/NewGameViewModel.cs | 10 +- .../ObservedChanged/NewGameViewModelTests.cs | 13 +- .../ObservedChangedMixinTest.cs | 46 +- .../ReactiveUI.Tests/OrderedComparerTest.cs | 7 +- .../Properties/Resources.Designer.cs | 2 +- .../Properties/Resources.resx | 2 +- ...opertyBinderImplementationAdvancedTests.cs | 345 +- .../PropertyBinderImplementationTests.cs | 164 +- .../ReactiveObjects/Mocks/AccountService.cs | 20 +- .../ReactiveObjects/Mocks/AccountUser.cs | 8 +- .../Mocks/OaphNameOfTestFixture.cs | 23 +- .../ReactiveObjects/Mocks/OaphTestFixture.cs | 22 +- .../ReactiveObjects/Mocks/Project.cs | 8 +- .../ReactiveObjects/Mocks/ProjectService.cs | 26 +- .../ReactiveObjects/Mocks/TestFixture.cs | 20 +- .../Mocks/WhenAnyTestFixture.cs | 106 +- .../ReactiveObjects/ReactiveObjectTests.cs | 148 +- .../ReactiveObjects/ReactiveRecordTests.cs | 108 +- .../Mocks/ReactivePropertyVM.cs | 65 +- .../Mocks/SubcribeTestViewModel.cs | 83 +- .../ReactivePropertyBasicTests.cs | 211 +- .../ReactivePropertyTest.cs | 396 +- .../ReactiveProperties/TestEnum.cs | 16 +- .../ReactiveUI.Tests/ReactiveUI.Tests.csproj | 43 +- src/tests/ReactiveUI.Tests/ReflectionTest.cs | 65 +- .../DependencyResolverRegistrarTests.cs | 226 +- .../INPCObservableForPropertyTests.cs | 114 +- .../PocoObservableForPropertyTests.cs | 72 +- .../ReactiveUI.Tests/RxAppBuilderTest.cs | 73 +- .../ReactiveUI.Tests/RxSchedulersTest.cs | 4 +- .../ReactiveUI.Tests/ScheduledSubjectTest.cs | 24 +- .../SchedulerConsumptionTest.cs | 61 +- .../Suspension/DummyAppState.cs | 9 +- .../Suspension/DummySuspensionDriverTests.cs | 2 +- .../SuspensionHostExtensionsAotTests.cs | 196 +- .../Suspension/SuspensionHostGenericTests.cs | 246 +- .../Suspension/SuspensionHostTests.cs | 71 +- .../SuspensionHostExtensionsTests.cs | 243 +- .../UnhandledInteractionExceptionTest.cs | 34 +- .../Utilities/CompatMixins.cs | 18 +- .../Utilities/CompatMixinsTests.cs | 30 +- .../Utilities/CountingTestScheduler.cs | 12 +- .../Utilities/DisposableMixinsTests.cs | 5 +- .../Utilities/EnumerableTestMixin.cs | 29 +- .../ReactiveUI.Tests/Utilities/JsonHelper.cs | 20 +- .../ReactiveUI.Tests/VariadicTemplatesTest.cs | 3046 --- .../WaitForDispatcherSchedulerTests.cs | 14 +- .../WhenAny/Mockups/HostTestFixture.cs | 39 +- .../Mockups/NonObservableTestFixture.cs | 8 +- .../WhenAny/Mockups/NonReactiveINPCObject.cs | 12 +- .../WhenAny/Mockups/ObjChain1.cs | 8 +- .../WhenAny/Mockups/ObjChain2.cs | 8 +- .../WhenAny/Mockups/ObjChain3.cs | 8 +- .../WhenAny/Mockups/OwnerClass.cs | 5 +- ...yChangedMixinTest.ObservableForProperty.cs | 764 + ...fyPropertyChangedMixinTest.WhenAnyValue.cs | 586 + .../ReactiveNotifyPropertyChangedMixinTest.cs | 1856 +- .../WhenAny/TestWhenAnyObsViewModel.cs | 27 +- .../WhenAny/WhenAnyArityTestViewModel.cs | 252 + .../WhenAny/WhenAnyMixinTests.Arity1.cs | 187 + .../WhenAny/WhenAnyMixinTests.Arity10.cs | 212 + .../WhenAny/WhenAnyMixinTests.Arity11.cs | 220 + .../WhenAny/WhenAnyMixinTests.Arity12.cs | 228 + .../WhenAny/WhenAnyMixinTests.Arity2.cs | 206 + .../WhenAny/WhenAnyMixinTests.Arity3.cs | 218 + .../WhenAny/WhenAnyMixinTests.Arity4.cs | 230 + .../WhenAny/WhenAnyMixinTests.Arity5.cs | 242 + .../WhenAny/WhenAnyMixinTests.Arity6.cs | 254 + .../WhenAny/WhenAnyMixinTests.Arity7.cs | 266 + .../WhenAny/WhenAnyMixinTests.Arity8.cs | 196 + .../WhenAny/WhenAnyMixinTests.Arity9.cs | 204 + .../WhenAnyObservableMixinTests.Arity1.cs | 28 + .../WhenAnyObservableMixinTests.Arity10.cs | 123 + .../WhenAnyObservableMixinTests.Arity11.cs | 131 + .../WhenAnyObservableMixinTests.Arity12.cs | 139 + .../WhenAnyObservableMixinTests.Arity2.cs | 55 + .../WhenAnyObservableMixinTests.Arity3.cs | 63 + .../WhenAnyObservableMixinTests.Arity4.cs | 71 + .../WhenAnyObservableMixinTests.Arity5.cs | 79 + .../WhenAnyObservableMixinTests.Arity6.cs | 87 + .../WhenAnyObservableMixinTests.Arity7.cs | 95 + .../WhenAnyObservableMixinTests.Arity8.cs | 107 + .../WhenAnyObservableMixinTests.Arity9.cs | 115 + .../WhenAny/WhenAnyObservableTests.cs | 264 +- .../WhenAnyDynamicTest.HighArity.cs | 637 + .../ReactiveUI.Tests/WhenAnyDynamicTest.cs | 709 +- src/tests/ReactiveUI.Tests/app.config | 2 +- ...ovalTests.Winforms.DotNet10_0.verified.txt | 2 +- ...rovalTests.Winforms.DotNet8_0.verified.txt | 2 +- ...rovalTests.Winforms.DotNet9_0.verified.txt | 2 +- .../API/ApiApprovalTests.cs | 4 +- .../AssemblyHooks.cs | 3 +- .../AssemblyInfo.Parallel.cs | 2 +- .../ReactiveUI.WinForms.Tests.csproj | 71 +- .../ReactiveUIBuilderWinFormsTests.cs | 13 +- .../winforms/ActivationTests.cs | 129 +- .../winforms/CanActivateViewFetcherTests.cs | 16 +- .../CommandBindingImplementationTests.cs | 40 +- .../winforms/CommandBindingTests.cs | 14 +- .../ContentControlBindingHookTests.cs | 26 +- .../CreatesWinformsCommandBindingTests.cs | 168 +- .../winforms/DefaultPropertyBindingTests.cs | 54 +- .../winforms/Mocks/AnotherView.cs | 3 +- .../winforms/Mocks/ContractExampleView.cs | 2 +- .../Mocks/CustomClickableComponent.cs | 2 +- .../CustomClickableComponentWithEnabled.cs | 2 +- .../winforms/Mocks/CustomClickableControl.cs | 2 +- .../winforms/Mocks/CustomEventArgs.cs | 2 +- .../winforms/Mocks/EnabledComponent.cs | 2 +- .../winforms/Mocks/ExampleView.cs | 3 +- .../winforms/Mocks/ExampleWindowView.cs | 3 +- .../winforms/Mocks/FakeView.cs | 2 +- .../winforms/Mocks/FakeViewLocator.cs | 46 +- .../winforms/Mocks/FakeViewModel.cs | 2 +- .../winforms/Mocks/FakeWinformViewModel.cs | 36 +- .../winforms/Mocks/FakeWinformsView.cs | 2 +- .../winforms/Mocks/GenericEventControl.cs | 2 +- .../winforms/Mocks/NeverUsedView.cs | 2 +- .../winforms/Mocks/NoClickEventComponent.cs | 2 +- .../Mocks/ReactiveCommandOutputViewModel.cs | 2 +- .../Mocks/SingleInstanceExampleView.cs | 2 +- .../SingleInstanceWithContractExampleView.cs | 11 +- .../winforms/Mocks/TestControl.cs | 5 +- .../winforms/Mocks/TestForm.cs | 50 +- .../winforms/Mocks/TestForm.resx | 47 +- .../winforms/Mocks/TestFormNotCanActivate.cs | 5 +- .../Mocks/TestFormNotCanActivate.resx | 47 +- .../winforms/Mocks/ThirdPartyControl.cs | 25 +- .../winforms/Mocks/ViewWithoutMatchingName.cs | 6 +- .../winforms/Mocks/WinformCommandBindView.cs | 14 +- .../Mocks/WinformCommandBindViewModel.cs | 51 +- ...tionChangedToListChangedTransformerTest.cs | 47 +- .../PanelSetMethodBindingConverterTests.cs | 37 +- .../winforms/PlatformOperationsTest.cs | 2 +- .../ReactiveCommandWinFormsOutputTests.cs | 87 +- .../ReactiveUserControlNonGenericTest.cs | 13 +- .../winforms/ReactiveUserControlTest.cs | 15 +- ...leContentSetMethodBindingConverterTests.cs | 40 +- ...WinFormsReactiveUIBuilderExtensionsTest.cs | 2 +- .../winforms/WinFormsRoutedViewHostTests.cs | 25 +- .../winforms/WinFormsTestExecutor.cs | 5 +- .../WinFormsViewDependencyResolverTests.cs | 51 +- .../WinFormsViewModelViewHostTests.cs | 33 +- ...nformsCreatesObservableForPropertyTests.cs | 23 +- .../winforms/WinformsViewsTestExecutor.cs | 5 +- ...pprovalTests.Blend.DotNet10_0.verified.txt | 2 +- ...ApprovalTests.Blend.DotNet8_0.verified.txt | 2 +- ...ApprovalTests.Blend.DotNet9_0.verified.txt | 2 +- ...iApprovalTests.Wpf.DotNet10_0.verified.txt | 2 +- ...piApprovalTests.Wpf.DotNet8_0.verified.txt | 2 +- ...piApprovalTests.Wpf.DotNet9_0.verified.txt | 2 +- .../API/ApiApprovalTests.cs | 6 +- .../ReactiveUI.Wpf.Tests/AssemblyHooks.cs | 3 +- .../AssemblyInfo.Parallel.cs | 2 +- .../ObservableAsPropertyHelperModeTests.cs | 2 +- .../ReactiveObjects/Mocks/TestFixture.cs | 20 +- .../ReactiveUI.Wpf.Tests.csproj | 71 +- .../Utilities/EnumerableTestMixin.cs | 23 +- .../WhenAny/Mockups/HostTestFixture.cs | 36 +- .../Mockups/NonObservableTestFixture.cs | 8 +- .../WhenAny/Mockups/OwnerClass.cs | 8 +- .../Wpf/ActivationForViewFetcherTest.cs | 13 +- .../Wpf/AutoSuspendHelperTest.cs | 2 +- .../BooleanToVisibilityTypeConverterTest.cs | 11 +- .../Wpf/DefaultViewLocatorTests.cs | 14 +- .../Wpf/FollowObservableStateBehaviorTests.cs | 22 +- .../CanExecuteMock/AlwaysFalseModeDetector.cs | 2 +- .../CanExecuteExecutingView.xaml | 2 +- .../CanExecuteExecutingView.xaml.cs | 9 +- .../Mocks/CanExecuteMock/LiveModeDetector.cs | 26 +- .../Wpf/Mocks/CommandBindingView.cs | 20 +- .../Wpf/Mocks/CommandBindingViewModel.cs | 68 +- .../Wpf/Mocks/ExampleWindowView.cs | 5 +- .../Wpf/Mocks/FakeXamlCommandBindingView.cs | 2 +- .../Wpf/Mocks/TransitionMock/FirstView.xaml | 2 +- .../Mocks/TransitionMock/FirstView.xaml.cs | 5 +- .../Wpf/Mocks/TransitionMock/SecondView.xaml | 2 +- .../Mocks/TransitionMock/SecondView.xaml.cs | 5 +- .../Mocks/TransitionMock/TCMockWindow.xaml | 2 +- .../Mocks/TransitionMock/TCMockWindow.xaml.cs | 14 +- .../TransitionMock/TCMockWindowViewModel.cs | 8 +- .../FakeViewWithContract.cs | 39 +- .../Wpf/Mocks/WpfTestUserControl.cs | 5 +- .../Wpf/ObservableTriggerTests.cs | 25 +- .../Wpf/PlatformOperationsTest.cs | 2 +- .../Wpf/ReactivePageTest.cs | 7 +- .../Wpf/ReactivePropertyMixinsTests.cs | 145 +- .../Wpf/ReactiveUIBuilderWpfTests.cs | 13 +- .../Wpf/ReactiveUserControlTest.cs | 7 +- .../Wpf/ReactiveWindowTest.cs | 7 +- .../Wpf/TransitioningContentControlTest.cs | 209 +- .../Wpf/ValidationBindingMixinsTest.cs | 22 +- .../Wpf/ValidationBindingWpfTest.cs | 147 +- .../VisibilityToBooleanTypeConverterTests.cs | 11 +- .../Wpf/WpfActivationForViewFetcherTest.cs | 48 +- .../Wpf/WpfActiveContentTests.cs | 54 +- .../WpfCommandBindingImplementationTests.cs | 70 +- .../Wpf/WpfReactiveUIBuilderExtensionsTest.cs | 2 +- .../Wpf/WpfTestExecutor.cs | 5 +- .../Wpf/WpfViewDependencyResolverTests.cs | 7 +- ...pprovalTests.Blend.DotNet10_0.verified.txt | 2 +- ...ApprovalTests.Blend.DotNet8_0.verified.txt | 2 +- ...ApprovalTests.Blend.DotNet9_0.verified.txt | 2 +- ...ApiApprovalTests.Blend.Net4_7.verified.txt | 2 +- .../Xaml/Api/XamlApiApprovalTests.cs | 2 +- .../Xaml/CommandBindingImplementationTests.cs | 50 +- ...pendencyObjectObservableForPropertyTest.cs | 70 +- .../ReactiveUI.Wpf.Tests/Xaml/MockWindow.xaml | 2 +- .../Xaml/MockWindow.xaml.cs | 11 +- .../Xaml/Mocks/AnotherView.cs | 2 +- .../Xaml/Mocks/AnotherViewModel.cs | 5 +- .../Xaml/Mocks/CommandBindView.cs | 2 +- .../Xaml/Mocks/CommandBindViewModel.cs | 12 +- .../Xaml/Mocks/CustomClickButton.cs | 2 +- .../Xaml/Mocks/DepObjFixture.cs | 2 +- .../Xaml/Mocks/DerivedDepObjFixture.cs | 2 +- .../Xaml/Mocks/ExampleView.cs | 2 +- .../Xaml/Mocks/ExampleViewContract.cs | 2 +- .../Xaml/Mocks/ExampleViewModel.cs | 5 +- .../Xaml/Mocks/ExampleWindowViewModel.cs | 5 +- .../Xaml/Mocks/FakeNestedViewModel.cs | 2 +- .../Xaml/Mocks/FakeView.cs | 2 +- .../Xaml/Mocks/FakeViewModel.cs | 2 +- .../Xaml/Mocks/HostTestView.cs | 2 +- .../Xaml/Mocks/IRoutableFooViewModel.cs | 2 +- .../Xaml/Mocks/MockBindListItemViewModel.cs | 12 +- .../Xaml/Mocks/MockBindListView.cs | 9 +- .../Xaml/Mocks/MockBindListViewModel.cs | 26 +- .../Xaml/Mocks/PropertyBindFakeControl.cs | 2 +- .../Xaml/Mocks/PropertyBindModel.cs | 2 +- .../Xaml/Mocks/PropertyBindView.cs | 2 +- .../Xaml/Mocks/PropertyBindViewModel.cs | 118 +- .../Mocks/ReactiveObjectCommandBindView.cs | 5 +- .../Xaml/Mocks/RoutableFooCustomView.cs | 2 +- .../Xaml/Mocks/RoutableFooView.cs | 2 +- .../Xaml/Mocks/TestView.cs | 13 +- .../Xaml/Mocks/TestViewModel.cs | 11 +- .../Xaml/Mocks/View1.xaml | 2 +- .../Xaml/Mocks/View1.xaml.cs | 2 +- .../Xaml/Mocks/View2.xaml | 2 +- .../Xaml/Mocks/View2.xaml.cs | 2 +- .../Xaml/Mocks/ViewModelWithWeirdName.cs | 5 +- .../Xaml/Mocks/ViewWithWeirdName.cs | 2 +- .../Xaml/PropertyBindingTest.Converters.cs | 602 + .../PropertyBindingTest.IntegerConverters.cs | 648 + .../Xaml/PropertyBindingTest.cs | 1208 +- .../Xaml/RoutableFooViewModel.cs | 2 +- .../Xaml/RoutableViewModelMixinTests.cs | 22 +- .../Xaml/RoutedViewHostTests.cs | 25 +- .../Xaml/RoutingStateTests.cs | 153 +- .../Xaml/RxAppDependencyObjectTests.cs | 2 +- .../Xaml/Utilities/DispatcherUtilities.cs | 2 +- .../Xaml/ViewModelViewHostTests.cs | 22 +- .../WhenAnyThroughDependencyObjectTests.cs | 12 +- .../Xaml/WpfViewResolverTestExecutor.cs | 5 +- .../Xaml/XamlViewCommandTests.cs | 2 +- .../Xaml/XamlViewDependencyResolverTests.cs | 4 +- version.json | 21 - 1245 files changed, 125401 insertions(+), 78140 deletions(-) create mode 100644 .github/workflows/sonarcloud.yml create mode 100644 CONTRIBUTING_PERFORMANCE.md create mode 100644 MERGE_FOLLOWUPS.md create mode 100644 src/ReactiveUI/Bindings/BindingAffinity.cs create mode 100644 src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.Conversions.cs create mode 100644 src/ReactiveUI/ChangeSets/ChangeSetExtensions.cs create mode 100644 src/ReactiveUI/ChangeSets/CollectionChanged.cs create mode 100644 src/ReactiveUI/ChangeSets/CollectionChangedExtensions.cs create mode 100644 src/ReactiveUI/ChangeSets/IReactiveChangeSet.cs create mode 100644 src/ReactiveUI/ChangeSets/IReactiveChangeSet{T}.cs create mode 100644 src/ReactiveUI/ChangeSets/ReactiveChange.cs create mode 100644 src/ReactiveUI/ChangeSets/ReactiveChangeReason.cs create mode 100644 src/ReactiveUI/Comparers/OrderedComparer{T}.cs create mode 100644 src/ReactiveUI/Internal/Broadcaster.cs create mode 100644 src/ReactiveUI/Internal/DelegateObserver.cs create mode 100644 src/ReactiveUI/Internal/Disposables/ActionDisposable.cs create mode 100644 src/ReactiveUI/Internal/Disposables/DisposableBag.cs create mode 100644 src/ReactiveUI/Internal/Disposables/DisposableSlotHelper.cs create mode 100644 src/ReactiveUI/Internal/Disposables/EmptyDisposable.cs create mode 100644 src/ReactiveUI/Internal/Disposables/MutableDisposable.cs create mode 100644 src/ReactiveUI/Internal/Disposables/OnceDisposable.cs create mode 100644 src/ReactiveUI/Internal/Disposables/SwapDisposable.cs create mode 100644 src/ReactiveUI/Internal/EventPatternObservable.cs create mode 100644 src/ReactiveUI/Internal/ExpressionChainParameters{TSender}.cs create mode 100644 src/ReactiveUI/Internal/ExpressionChainSink.cs create mode 100644 src/ReactiveUI/Internal/InteractionHandleObservable.cs create mode 100644 src/ReactiveUI/Internal/NeverObservable.cs create mode 100644 src/ReactiveUI/Internal/ObservableForPropertySink.cs create mode 100644 src/ReactiveUI/Internal/ObservedChangeValueSelector.cs create mode 100644 src/ReactiveUI/Internal/SingleValueObservable.cs create mode 100644 src/ReactiveUI/Internal/SingleValueObservable{T}.cs create mode 100644 src/ReactiveUI/Internal/StartObservable.cs create mode 100644 src/ReactiveUI/Internal/Subjects/BehaviorBroadcastSubject.cs create mode 100644 src/ReactiveUI/Internal/Subjects/BroadcastSubject.cs create mode 100644 src/ReactiveUI/Internal/Subjects/DelayableNotificationSubject.cs create mode 100644 src/ReactiveUI/Internal/Subjects/ReplayBroadcastSubject.cs create mode 100644 src/ReactiveUI/Internal/Subjects/SchedulingObserver.cs create mode 100644 src/ReactiveUI/Internal/SyncExecuteObservable.cs create mode 100644 src/ReactiveUI/Internal/TaskObservable.cs create mode 100644 src/ReactiveUI/Internal/TaskUnitObservable.cs create mode 100644 src/ReactiveUI/Internal/ThrowObservable.cs create mode 100644 src/ReactiveUI/Internal/ToUnitObservable.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/EmptyObservable.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity1.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity10.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity11.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity12.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity2.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity3.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity4.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity5.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity6.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity7.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity8.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity9.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyObservableMergeSink.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyObservableSwitchSink.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity1.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity10.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity11.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity12.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity2.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity3.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity4.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity5.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity6.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity7.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity8.cs create mode 100644 src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity9.cs delete mode 100644 src/ReactiveUI/Observables.cs create mode 100644 src/ReactiveUI/Polyfills/HashCode.cs create mode 100644 src/ReactiveUI/Polyfills/Index.cs create mode 100644 src/ReactiveUI/Polyfills/Range.cs create mode 100644 src/ReactiveUI/ReactiveCommand/ReactiveCommand{TParam,TResult}.cs create mode 100644 src/ReactiveUI/Subjects/IReactiveSubject{T}.cs create mode 100644 src/ReactiveUI/Temp/CompositeDisposable.cs create mode 100644 src/ReactiveUI/Temp/DisposableMixins.cs create mode 100644 src/ReactiveUI/Temp/ICancelable.cs create mode 100644 src/ReactiveUI/Temp/IScheduler.cs create mode 100644 src/ReactiveUI/Temp/ISubject.cs create mode 100644 src/ReactiveUI/Temp/Schedulers.cs create mode 100644 src/ReactiveUI/Temp/Unit.cs delete mode 100644 src/ReactiveUI/VariadicTemplates.cs delete mode 100644 src/ReactiveUI/VariadicTemplates.tt create mode 100644 src/ReactiveUI/WhenAny/ObservableExtensions.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity1.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity10.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity11.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity12.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity2.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity3.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity4.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity5.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity6.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity7.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity8.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyMixin.Arity9.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity1.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity10.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity11.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity12.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity2.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity3.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity4.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity5.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity6.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity7.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity8.cs create mode 100644 src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity9.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsHappyPathTests.HighArity.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsTests.Arity01to06.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsTests.Arity07to09.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsTests.Arity10to12.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsTests.Arity13to14.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsTests.Arity15to16.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsTests.HelpersAssert.cs create mode 100644 src/tests/ReactiveUI.Builder.Tests/Mixins/BuilderInstanceMixinsTests.HelpersInvoke.cs delete mode 100644 src/tests/ReactiveUI.Builder.Tests/TestConfiguration.cs delete mode 100644 src/tests/ReactiveUI.Testing.Tests/TestConfiguration.cs rename src/tests/ReactiveUI.Tests/Commands/Mocks/{ICommandHolder.cs => CommandHolder.cs} (82%) create mode 100644 src/tests/ReactiveUI.Tests/Commands/ReactiveCommandTest.CreationTasks.cs create mode 100644 src/tests/ReactiveUI.Tests/Commands/ReactiveCommandTest.ExecutionAndInvoke.cs create mode 100644 src/tests/ReactiveUI.Tests/Commands/ReactiveCommandTest.IsExecutingAndExceptions.cs delete mode 100644 src/tests/ReactiveUI.Tests/VariadicTemplatesTest.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/ReactiveNotifyPropertyChangedMixinTest.ObservableForProperty.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/ReactiveNotifyPropertyChangedMixinTest.WhenAnyValue.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyArityTestViewModel.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity1.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity10.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity11.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity12.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity2.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity3.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity4.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity5.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity6.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity7.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity8.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyMixinTests.Arity9.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity1.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity10.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity11.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity12.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity2.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity3.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity4.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity5.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity6.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity7.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity8.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAny/WhenAnyObservableMixinTests.Arity9.cs create mode 100644 src/tests/ReactiveUI.Tests/WhenAnyDynamicTest.HighArity.cs create mode 100644 src/tests/ReactiveUI.Wpf.Tests/Xaml/PropertyBindingTest.Converters.cs create mode 100644 src/tests/ReactiveUI.Wpf.Tests/Xaml/PropertyBindingTest.IntegerConverters.cs delete mode 100644 version.json diff --git a/.editorconfig b/.editorconfig index 77a6c4013d..506d75b461 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,8 @@ root = true # Default settings ############################################# [*] +# UTF-8 without a byte-order mark (the "utf-8-bom" value would add one). +charset = utf-8 insert_final_newline = true indent_style = space indent_size = 4 @@ -78,8 +80,15 @@ dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.required_modifiers = const dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Local constants should be PascalCase +dotnet_naming_rule.local_constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_constants_should_be_pascal_case.symbols = local_constants +dotnet_naming_rule.local_constants_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.local_constants.applicable_kinds = local +dotnet_naming_symbols.local_constants.required_modifiers = const + # Static fields should have s_ prefix -dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.severity = none dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style dotnet_naming_symbols.static_fields.applicable_kinds = field @@ -105,31 +114,20 @@ dotnet_sort_system_directives_first = true csharp_prefer_braces = true:silent csharp_preserve_single_line_blocks = true:none csharp_preserve_single_line_statements = false:none -csharp_prefer_static_local_function = true:suggestion -csharp_prefer_simple_using_statement = false:none -csharp_style_prefer_switch_expression = true:suggestion ################### # Code quality ################### -dotnet_style_readonly_field = true:suggestion dotnet_code_quality_unused_parameters = non_public:suggestion ################### # Expression-level preferences ################### -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_coalesce_expression = true:suggestion dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_auto_properties = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:silent dotnet_style_prefer_conditional_expression_over_return = true:silent -csharp_prefer_simple_default_expression = true:suggestion ################### # Expression-bodied members @@ -143,19 +141,6 @@ csharp_style_expression_bodied_accessors = true:suggestion csharp_style_expression_bodied_lambdas = true:suggestion csharp_style_expression_bodied_local_functions = true:suggestion -################### -# Pattern matching -################### -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion - -################### -# Null checking preferences -################### -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion - ################### # Other features ################### @@ -201,18 +186,49 @@ dotnet_diagnostic.AvoidAsyncVoid.severity = suggestion ################### # Microsoft .NET Analyzers (CA) - Design Rules ################### -dotnet_diagnostic.CA1000.severity = none # Do not declare static members on generic types +dotnet_diagnostic.CA1000.severity = none # Do not declare static members on generic types — common factory pattern dotnet_diagnostic.CA1001.severity = error # Types that own disposable fields should be disposable +dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists — we deliberately expose List; interface-based collections are an older convention we don't follow +dotnet_diagnostic.CA1003.severity = error # Use generic event handler instances +dotnet_diagnostic.CA1005.severity = error # Avoid excessive parameters on generic types +dotnet_diagnostic.CA1008.severity = error # Enums should have zero value +dotnet_diagnostic.CA1010.severity = none # Collections should implement generic interface — we deliberately expose concrete collection types; interface-based collections are an older convention we don't follow +dotnet_diagnostic.CA1012.severity = error # Abstract types should not have public constructors +dotnet_diagnostic.CA1014.severity = none # Mark assemblies with CLSCompliantAttribute — we don't ship CLS-compliant assemblies dotnet_diagnostic.CA1016.severity = error # Mark assemblies with AssemblyVersionAttribute +dotnet_diagnostic.CA1017.severity = none # Mark assemblies with ComVisibleAttribute — we don't ship COM-visible assemblies +dotnet_diagnostic.CA1018.severity = error # Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1019.severity = error # Define accessors for attribute arguments +dotnet_diagnostic.CA1021.severity = none # Avoid out parameters - disabled - needed for the zero-allocation idiom in Try/Find APIs and other performance-critical paths +dotnet_diagnostic.CA1024.severity = error # Use properties where appropriate dotnet_diagnostic.CA1027.severity = error # Mark enums with FlagsAttribute -dotnet_diagnostic.CA1030.severity = none # Use events where appropriate -dotnet_diagnostic.CA1031.severity = none # Do not catch general exception types -dotnet_diagnostic.CA1033.severity = none # Interface methods should be callable by child types -dotnet_diagnostic.CA1036.severity = none # Override methods on comparable types +dotnet_diagnostic.CA1028.severity = error # Enum storage should be Int32 +dotnet_diagnostic.CA1030.severity = none # Use events where appropriate — we use Rx observables instead of CLR events +dotnet_diagnostic.CA1031.severity = none # Do not catch general exception types — required at logging/dispose/IO boundaries +dotnet_diagnostic.CA1032.severity = error # Implement standard exception constructors +dotnet_diagnostic.CA1033.severity = none # Interface methods should be callable by child types — explicit interface implementations are a deliberate design choice +dotnet_diagnostic.CA1034.severity = none # Nested types should not be visible — public nested types are sometimes the cleanest API (e.g. interface-scoped exception helpers) +dotnet_diagnostic.CA1036.severity = none # Override methods on comparable types — relational operators rarely meaningful for our types +dotnet_diagnostic.CA1040.severity = error # Avoid empty interfaces +dotnet_diagnostic.CA1041.severity = error # Provide ObsoleteAttribute message +dotnet_diagnostic.CA1043.severity = error # Use integral or string argument for indexers +dotnet_diagnostic.CA1044.severity = error # Properties should not be write only +dotnet_diagnostic.CA1045.severity = error # Do not pass types by reference +dotnet_diagnostic.CA1046.severity = error # Do not overload operator equals on reference types +dotnet_diagnostic.CA1047.severity = error # Do not declare protected member in sealed type +dotnet_diagnostic.CA1048.severity = error # Do not declare virtual members in sealed types +dotnet_diagnostic.CA1050.severity = error # Declare types in namespaces +dotnet_diagnostic.CA1051.severity = error # Do not declare visible instance fields +dotnet_diagnostic.CA1052.severity = error # Static holder types should be sealed +dotnet_diagnostic.CA1053.severity = error # Static holder types should not have constructors +dotnet_diagnostic.CA1054.severity = suggestion # URI parameters should not be strings +dotnet_diagnostic.CA1055.severity = suggestion # URI return values should not be strings dotnet_diagnostic.CA1056.severity = suggestion # URI properties should not be strings +dotnet_diagnostic.CA1058.severity = error # Types should not extend certain base types +dotnet_diagnostic.CA1059.severity = error # Members should not expose certain concrete types dotnet_diagnostic.CA1060.severity = error # Move P/Invokes to NativeMethods class dotnet_diagnostic.CA1061.severity = error # Do not hide base class methods -dotnet_diagnostic.CA1062.severity = error # Validate arguments of public methods +dotnet_diagnostic.CA1062.severity = none # Validate arguments of public methods - Nullable=enable + we own every consumer, so the compiler already guarantees non-null params dotnet_diagnostic.CA1063.severity = error # Implement IDisposable correctly dotnet_diagnostic.CA1064.severity = error # Exceptions should be public dotnet_diagnostic.CA1065.severity = error # Do not raise exceptions in unexpected locations @@ -220,30 +236,13 @@ dotnet_diagnostic.CA1066.severity = error # Implement IEquatable when overriding dotnet_diagnostic.CA1067.severity = error # Override Equals when implementing IEquatable dotnet_diagnostic.CA1068.severity = error # CancellationToken parameters must come last dotnet_diagnostic.CA1069.severity = error # Enums should not have duplicate values -dotnet_diagnostic.CA2000.severity = suggestion # Dispose objects before losing scope -dotnet_diagnostic.CA2002.severity = error # Do not lock on objects with weak identity -dotnet_diagnostic.CA2011.severity = error # Do not assign property within its setter -dotnet_diagnostic.CA2012.severity = error # Use ValueTasks correctly -dotnet_diagnostic.CA2013.severity = error # Do not use ReferenceEquals with value types -dotnet_diagnostic.CA2014.severity = error # Do not use stackalloc in loops -dotnet_diagnostic.CA2015.severity = error # Do not define finalizers for types derived from MemoryManager -dotnet_diagnostic.CA2016.severity = error # Forward the CancellationToken parameter to methods that take one -dotnet_diagnostic.CA2200.severity = error # Rethrow to preserve stack details -dotnet_diagnostic.CA2213.severity = error # Disposable fields should be disposed -dotnet_diagnostic.CA2214.severity = error # Do not call overridable methods in constructors -dotnet_diagnostic.CA2216.severity = error # Disposable types should declare finalizer -dotnet_diagnostic.CA2229.severity = error # Implement serialization constructors -dotnet_diagnostic.CA2231.severity = error # Overload operator equals on overriding ValueType.Equals -dotnet_diagnostic.CA2235.severity = error # Mark all non-serializable fields -dotnet_diagnostic.CA2237.severity = error # Mark ISerializable types with SerializableAttribute -dotnet_diagnostic.CA2241.severity = error # Provide correct arguments to formatting methods -dotnet_diagnostic.CA2242.severity = error # Test for NaN correctly +dotnet_diagnostic.CA1070.severity = error # Do not declare event fields as virtual ################### # Microsoft .NET Analyzers (CA) - Globalization Rules ################### -dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters -dotnet_diagnostic.CA1308.severity = none # Normalize strings to uppercase +dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters — we don't ship localized resources +dotnet_diagnostic.CA1308.severity = none # Normalize strings to uppercase — ToLowerInvariant is correct for filesystem path / cache key normalization ################### # Microsoft .NET Analyzers (CA) - Interoperability Rules @@ -253,44 +252,178 @@ dotnet_diagnostic.CA1401.severity = error # P/Invokes should not be visible ################### # Microsoft .NET Analyzers (CA) - Maintainability Rules ################### +dotnet_diagnostic.CA1500.severity = error # Variable names should not match field names +dotnet_diagnostic.CA1501.severity = none # Avoid excessive inheritance — disabled because the analyzer noticeably slows down the build +dotnet_diagnostic.CA1502.severity = none # Avoid excessive complexity — disabled because the analyzer noticeably slows down the build +dotnet_diagnostic.CA1505.severity = error # Avoid unmaintainable code +dotnet_diagnostic.CA1506.severity = none # Avoid excessive class coupling — adds little signal here, mostly trips on legitimate orchestration code dotnet_diagnostic.CA1507.severity = error # Use nameof in place of string +dotnet_diagnostic.CA1508.severity = error # Avoid dead conditional code +dotnet_diagnostic.CA1509.severity = error # Invalid entry in code metrics configuration file +dotnet_diagnostic.CA1510.severity = none # Use ArgumentNullException throw helper — disabled because we target older TFMs and use ArgumentExceptionHelper for cross-platform parity +dotnet_diagnostic.CA1511.severity = none # Use ArgumentException throw helper — disabled because we target older TFMs and use ArgumentExceptionHelper for cross-platform parity +dotnet_diagnostic.CA1512.severity = none # Use ArgumentOutOfRangeException throw helper — disabled because we target older TFMs and use ArgumentExceptionHelper for cross-platform parity +dotnet_diagnostic.CA1513.severity = none # Use ObjectDisposedException throw helper — disabled because we target older TFMs and use ArgumentExceptionHelper for cross-platform parity +dotnet_diagnostic.CA1514.severity = error # Avoid redundant length argument +dotnet_diagnostic.CA1515.severity = none # Consider making public types internal — interferes with tests and reflection-discovered types (BenchmarkDotNet, TUnit, etc.) +dotnet_diagnostic.CA1516.severity = error # Use cross-platform intrinsics ################### # Microsoft .NET Analyzers (CA) - Naming Rules ################### dotnet_diagnostic.CA1710.severity = suggestion # Identifiers should have correct suffix -dotnet_diagnostic.CA1724.severity = none # Type Names Should Not Match Namespaces +dotnet_diagnostic.CA1724.severity = none # Type Names Should Not Match Namespaces — namespace/type name overlap is intentional API surface ################### # Microsoft .NET Analyzers (CA) - Performance Rules ################### -dotnet_diagnostic.CA1802.severity = error # Use Literals Where Appropriate +dotnet_diagnostic.CA1802.severity = error # Use literals where appropriate dotnet_diagnostic.CA1805.severity = error # Do not initialize unnecessarily -dotnet_diagnostic.CA1810.severity = none # Initialize reference type static fields inline +dotnet_diagnostic.CA1806.severity = error # Do not ignore method results +dotnet_diagnostic.CA1810.severity = none # Initialize reference type static fields inline — explicit static constructors are deliberate in some types dotnet_diagnostic.CA1812.severity = error # Avoid uninstantiated internal classes dotnet_diagnostic.CA1813.severity = error # Avoid unsealed attributes dotnet_diagnostic.CA1814.severity = error # Prefer jagged arrays over multidimensional dotnet_diagnostic.CA1815.severity = error # Override equals and operator equals on value types +dotnet_diagnostic.CA1819.severity = none # Properties should not return arrays — incompatible with the RxUI/sqlite-net mapping style we use throughout the codebase +dotnet_diagnostic.CA1820.severity = error # Test for empty strings using string length dotnet_diagnostic.CA1821.severity = error # Remove empty finalizers dotnet_diagnostic.CA1822.severity = error # Mark members as static +dotnet_diagnostic.CA1823.severity = error # Avoid unused private fields +dotnet_diagnostic.CA1824.severity = error # Mark assemblies with NeutralResourcesLanguageAttribute dotnet_diagnostic.CA1825.severity = error # Avoid zero-length array allocations dotnet_diagnostic.CA1826.severity = error # Use property instead of Linq Enumerable method dotnet_diagnostic.CA1827.severity = error # Do not use Count/LongCount when Any can be used dotnet_diagnostic.CA1828.severity = error # Do not use CountAsync/LongCountAsync when AnyAsync can be used dotnet_diagnostic.CA1829.severity = error # Use Length/Count property instead of Enumerable.Count method dotnet_diagnostic.CA1830.severity = error # Prefer strongly-typed Append and Insert method overloads on StringBuilder -dotnet_diagnostic.CA1831.severity = error # Use AsSpan instead of Range-based indexers for string -dotnet_diagnostic.CA1832.severity = error # Use AsSpan or AsMemory instead of Range-based indexers for ReadOnlySpan/Memory -dotnet_diagnostic.CA1833.severity = error # Use AsSpan or AsMemory instead of Range-based indexers for Span/Memory +dotnet_diagnostic.CA1831.severity = error # Use AsSpan instead of Range-based indexers for string when appropriate +dotnet_diagnostic.CA1832.severity = error # Use AsSpan or AsMemory instead of Range-based indexers for getting ReadOnlySpan or ReadOnlyMemory portion of an array +dotnet_diagnostic.CA1833.severity = error # Use AsSpan or AsMemory instead of Range-based indexers for getting Span or Memory portion of an array dotnet_diagnostic.CA1834.severity = error # Use StringBuilder.Append(char) for single character strings -dotnet_diagnostic.CA1835.severity = error # Prefer Memory-based overloads for ReadAsync and WriteAsync +dotnet_diagnostic.CA1835.severity = error # Prefer the memory-based overloads of ReadAsync/WriteAsync methods in stream-based classes dotnet_diagnostic.CA1836.severity = error # Prefer IsEmpty over Count when available dotnet_diagnostic.CA1837.severity = error # Use Environment.ProcessId instead of Process.GetCurrentProcess().Id dotnet_diagnostic.CA1838.severity = error # Avoid StringBuilder parameters for P/Invokes -dotnet_diagnostic.CA2007.severity = none # Do not directly await a Task +dotnet_diagnostic.CA1839.severity = error # Use Environment.ProcessPath instead of Process.GetCurrentProcess().MainModule.FileName +dotnet_diagnostic.CA1840.severity = error # Use Environment.CurrentManagedThreadId instead of Thread.CurrentThread.ManagedThreadId +dotnet_diagnostic.CA1841.severity = error # Prefer Dictionary.Contains methods +dotnet_diagnostic.CA1842.severity = error # Do not use 'WhenAll' with a single task +dotnet_diagnostic.CA1843.severity = error # Do not use 'WaitAll' with a single task +dotnet_diagnostic.CA1844.severity = error # Provide memory-based overrides of async methods when subclassing 'Stream' +dotnet_diagnostic.CA1845.severity = error # Use span-based 'string.Concat' +dotnet_diagnostic.CA1846.severity = error # Prefer AsSpan over Substring +dotnet_diagnostic.CA1847.severity = none # Use char literal for a single character lookup — disabled because the string.Contains(char) overload doesn't exist on .NET Framework / netstandard2.0 and we target both +dotnet_diagnostic.CA1848.severity = error # Use the LoggerMessage delegates +dotnet_diagnostic.CA1849.severity = error # Call async methods when in an async method +dotnet_diagnostic.CA1850.severity = error # Prefer static HashData method over ComputeHash +dotnet_diagnostic.CA1851.severity = error # Possible multiple enumerations of IEnumerable collection +dotnet_diagnostic.CA1852.severity = error # Seal internal types +dotnet_code_quality.CA1852.api_surface = private, internal # only flag non-public classes; public classes stay open for inheritance +dotnet_diagnostic.CA1853.severity = error # Unnecessary call to 'Dictionary.ContainsKey(key)' +dotnet_diagnostic.CA1854.severity = error # Prefer the IDictionary.TryGetValue(TKey, out TValue) method +dotnet_diagnostic.CA1855.severity = error # Prefer 'Clear' over 'Fill' +dotnet_diagnostic.CA1856.severity = error # Incorrect usage of ConstantExpected attribute +dotnet_diagnostic.CA1857.severity = error # A constant is expected for the parameter +dotnet_diagnostic.CA1858.severity = error # Use 'StartsWith' instead of 'IndexOf' +dotnet_diagnostic.CA1859.severity = error # Use concrete types when possible for improved performance +dotnet_diagnostic.CA1860.severity = error # Avoid using 'Enumerable.Any()' extension method +dotnet_diagnostic.CA1861.severity = error # Avoid constant arrays as arguments +dotnet_diagnostic.CA1862.severity = error # Use the 'StringComparison' method overloads to perform case-insensitive string comparisons +dotnet_diagnostic.CA1863.severity = error # Use 'CompositeFormat' +dotnet_diagnostic.CA1864.severity = error # Prefer the 'IDictionary.TryAdd(TKey, TValue)' method +dotnet_diagnostic.CA1865.severity = none # Use char overload (string.StartsWith) — disabled because the string.StartsWith(char) overload doesn't exist on .NET Framework / netstandard2.0 and we target both +dotnet_diagnostic.CA1866.severity = none # Use char overload (string.EndsWith) — disabled because the string.EndsWith(char) overload doesn't exist on .NET Framework / netstandard2.0 and we target both +dotnet_diagnostic.CA1867.severity = none # Use char overload (string.IndexOf / string.LastIndexOf) — disabled because the char overloads don't exist on .NET Framework / netstandard2.0 and we target both +dotnet_diagnostic.CA1868.severity = error # Unnecessary call to 'Contains' for sets +dotnet_diagnostic.CA1869.severity = error # Cache and reuse 'JsonSerializerOptions' instances +dotnet_diagnostic.CA1870.severity = error # Use a cached 'SearchValues' instance +dotnet_diagnostic.CA1871.severity = error # Do not pass a nullable struct to 'ArgumentNullException.ThrowIfNull' +dotnet_diagnostic.CA1872.severity = error # Prefer 'Convert.ToHexString' and 'Convert.ToHexStringLower' over call chains based on 'BitConverter.ToString' +dotnet_diagnostic.CA1873.severity = error # Avoid potentially expensive evaluation of arguments to 'Debug.Assert' +dotnet_diagnostic.CA1874.severity = error # Use 'Regex.IsMatch' +dotnet_diagnostic.CA1875.severity = error # Use 'Regex.Count' +dotnet_diagnostic.CA1877.severity = error # Use 'Encoding.GetString' instead of 'Encoding.GetChars' + +################### +# Microsoft .NET Analyzers (CA) - Reliability Rules +################### +dotnet_diagnostic.CA2000.severity = suggestion # Dispose objects before losing scope +dotnet_diagnostic.CA2002.severity = error # Do not lock on objects with weak identity +dotnet_diagnostic.CA2007.severity = none # Do not directly await a Task — Rx and library callers drive synchronization context themselves dotnet_diagnostic.CA2008.severity = error # Do not create tasks without passing a TaskScheduler dotnet_diagnostic.CA2009.severity = error # Do not call ToImmutableCollection on an ImmutableCollection value +dotnet_diagnostic.CA2011.severity = error # Do not assign property within its setter +dotnet_diagnostic.CA2012.severity = error # Use ValueTasks correctly +dotnet_diagnostic.CA2013.severity = error # Do not use ReferenceEquals with value types +dotnet_diagnostic.CA2014.severity = error # Do not use stackalloc in loops +dotnet_diagnostic.CA2015.severity = error # Do not define finalizers for types derived from MemoryManager +dotnet_diagnostic.CA2016.severity = error # Forward the CancellationToken parameter to methods that take one +dotnet_diagnostic.CA2017.severity = error # Parameter count mismatch +dotnet_diagnostic.CA2018.severity = error # The 'count' argument to 'Buffer.BlockCopy' should specify the number of bytes to copy +dotnet_diagnostic.CA2019.severity = error # Improper 'ThreadStatic' field initialization +dotnet_diagnostic.CA2020.severity = error # Prevent behavioral change caused by built-in operators of IntPtr/UIntPtr +dotnet_diagnostic.CA2021.severity = error # Don't call Enumerable.Cast or Enumerable.OfType with incompatible types +dotnet_diagnostic.CA2022.severity = error # Avoid inexact read with 'Stream.Read' +dotnet_diagnostic.CA2023.severity = error # Invalid braces in message template +dotnet_diagnostic.CA2024.severity = error # Do not use 'StreamReader.EndOfStream' in async methods +dotnet_diagnostic.CA2025.severity = error # Do not pass 'IDisposable' instances into unawaited tasks +dotnet_diagnostic.CA2026.severity = error # Do not use methods or types annotated with [RequiresDynamicCode] in code that uses [RequiresDynamicCode] + +################### +# Microsoft .NET Analyzers (CA) - Usage Rules +################### +dotnet_diagnostic.CA1801.severity = error # Review unused parameters +dotnet_code_quality.CA1801.api_surface = private, internal # only flag non-public APIs so we don't break public signatures +dotnet_diagnostic.CA1816.severity = error # Call GC.SuppressFinalize correctly +dotnet_diagnostic.CA2200.severity = error # Rethrow to preserve stack details +dotnet_diagnostic.CA2201.severity = error # Do not raise reserved exception types dotnet_diagnostic.CA2207.severity = error # Initialize value type static fields inline +dotnet_diagnostic.CA2208.severity = none # Instantiate argument exceptions correctly — too sensitive: flags valid context-forwarding nameof(arg.Property) patterns +dotnet_diagnostic.CA2211.severity = error # Non-constant fields should not be visible +dotnet_diagnostic.CA2213.severity = error # Disposable fields should be disposed +dotnet_diagnostic.CA2214.severity = error # Do not call overridable methods in constructors +dotnet_diagnostic.CA2215.severity = error # Dispose methods should call base class dispose +dotnet_diagnostic.CA2216.severity = error # Disposable types should declare finalizer +dotnet_diagnostic.CA2217.severity = error # Do not mark enums with FlagsAttribute +dotnet_diagnostic.CA2218.severity = error # Override GetHashCode on overriding Equals +dotnet_diagnostic.CA2219.severity = error # Do not raise exceptions in finally clauses +dotnet_diagnostic.CA2224.severity = error # Override Equals on overloading operator equals +dotnet_diagnostic.CA2225.severity = error # Operator overloads have named alternates +dotnet_diagnostic.CA2226.severity = error # Operators should have symmetrical overloads +dotnet_diagnostic.CA2227.severity = none # Collection properties should be read only — settable collection properties are common in our DTOs and config types +dotnet_diagnostic.CA2231.severity = error # Overload operator equals on overriding ValueType.Equals +dotnet_diagnostic.CA2234.severity = error # Pass System.Uri objects instead of strings +dotnet_diagnostic.CA2241.severity = error # Provide correct arguments to formatting methods +dotnet_diagnostic.CA2242.severity = error # Test for NaN correctly +dotnet_diagnostic.CA2243.severity = error # Attribute string literals should parse correctly +dotnet_diagnostic.CA2244.severity = error # Do not duplicate indexed element initializations +dotnet_diagnostic.CA2245.severity = error # Do not assign a property to itself +dotnet_diagnostic.CA2246.severity = error # Do not assign a symbol and its member in the same statement +dotnet_diagnostic.CA2247.severity = error # Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum +dotnet_diagnostic.CA2248.severity = error # Provide correct enum argument to Enum.HasFlag +dotnet_diagnostic.CA2249.severity = error # Use String.Contains instead of String.IndexOf for substring checks +dotnet_diagnostic.CA2250.severity = error # Use ThrowIfCancellationRequested +dotnet_diagnostic.CA2251.severity = error # Use String.Equals over String.Compare +dotnet_diagnostic.CA2252.severity = error # Opt in to preview features before using them +dotnet_diagnostic.CA2253.severity = error # Named placeholders should not be numeric values +dotnet_diagnostic.CA2254.severity = error # Template should be a static expression +dotnet_diagnostic.CA2255.severity = error # The 'ModuleInitializer' attribute should not be used in libraries +dotnet_diagnostic.CA2256.severity = error # All members declared in parent interfaces must have an implementation in a 'DynamicInterfaceCastableImplementation' interface +dotnet_diagnostic.CA2257.severity = error # Members defined in a type with the 'DynamicInterfaceCastableImplementationAttribute' should be 'static' +dotnet_diagnostic.CA2258.severity = error # Providing a 'DynamicInterfaceCastableImplementation' interface in Visual Basic is unsupported +dotnet_diagnostic.CA2259.severity = error # 'ThreadStatic' only affects static fields +dotnet_diagnostic.CA2260.severity = error # Implement generic math interfaces correctly +dotnet_diagnostic.CA2261.severity = error # Do not use 'ConfigureAwaitOptions.SuppressThrowing' with 'Task' +dotnet_diagnostic.CA2262.severity = error # Set 'MaxResponseHeadersLength' properly +dotnet_diagnostic.CA2263.severity = error # Prefer generic overload when type is known +dotnet_diagnostic.CA2264.severity = error # Do not pass a non-nullable value to 'ArgumentNullException.ThrowIfNull' +dotnet_diagnostic.CA2265.severity = error # Do not compare Span to 'null' or 'default' +dotnet_diagnostic.CA2266.severity = error # Do not consume the result of 'Span.Enumerator.MoveNext()' more than once +dotnet_diagnostic.CA2267.severity = error # Member should not be marked with 'StaticAttribute' +dotnet_diagnostic.CA2268.severity = error # Use 'string.Equals(string, string, StringComparison)' instead of the implicit ordinal overload +# Skipped (deprecated ISerializable formatter): CA2229 Implement serialization constructors, +# CA2235 Mark all non-serializable fields, CA2237 Mark ISerializable types with SerializableAttribute. ################### # Microsoft .NET Analyzers (CA) - Security Rules @@ -345,25 +478,35 @@ dotnet_diagnostic.CA5362.severity = error # Potential reference cycle in deseria dotnet_diagnostic.CA5350.severity = error # Do not use weak cryptographic algorithms (SHA1, RIPEMD160, TripleDES) dotnet_diagnostic.CA5351.severity = error # Do not use broken cryptographic algorithms (MD5, DES, RC2) dotnet_diagnostic.CA5358.severity = error # Do not use unsafe cipher modes (ECB, OFB, CFB) +dotnet_diagnostic.CA5373.severity = error # Do not use obsolete key derivation function +dotnet_diagnostic.CA5379.severity = error # Ensure key derivation function algorithm is sufficiently strong dotnet_diagnostic.CA5384.severity = error # Do not use Digital Signature Algorithm (DSA) dotnet_diagnostic.CA5385.severity = error # Use RSA algorithm with sufficient key size (>= 2048 bits) +dotnet_diagnostic.CA5387.severity = error # Do not use weak key derivation function with insufficient iteration count +dotnet_diagnostic.CA5388.severity = error # Ensure sufficient iteration count when using weak key derivation function dotnet_diagnostic.CA5390.severity = error # Do not hard-code encryption key dotnet_diagnostic.CA5394.severity = error # Do not use insecure randomness (use RNGCryptoServiceProvider) dotnet_diagnostic.CA5401.severity = error # Do not use CreateEncryptor with non-default IV +dotnet_diagnostic.CA5402.severity = error # Use CreateEncryptor with the default IV dotnet_diagnostic.CA5403.severity = error # Do not hard-code certificate -dotnet_diagnostic.CA5373.severity = error # Do not use obsolete key derivation function # TLS/SSL Protocol Security dotnet_diagnostic.CA5359.severity = error # Do not disable certificate validation dotnet_diagnostic.CA5361.severity = error # Do not disable SChannel use of strong crypto dotnet_diagnostic.CA5364.severity = error # Do not use deprecated security protocols (TLS 1.0, TLS 1.1, SSL3) dotnet_diagnostic.CA5378.severity = error # Do not disable ServicePointManagerSecurityProtocols +dotnet_diagnostic.CA5380.severity = error # Do not add certificates to root store +dotnet_diagnostic.CA5381.severity = error # Ensure certificates are not added to root store dotnet_diagnostic.CA5386.severity = error # Avoid hardcoding SecurityProtocolType value dotnet_diagnostic.CA5397.severity = error # Do not use deprecated SslProtocols values dotnet_diagnostic.CA5398.severity = error # Avoid hardcoded SslProtocols values dotnet_diagnostic.CA5399.severity = error # Definitely disable HttpClient certificate revocation list check -dotnet_diagnostic.CA5380.severity = error # Do not add certificates to root store -dotnet_diagnostic.CA5381.severity = error # Ensure certificates are not added to root store +dotnet_diagnostic.CA5400.severity = error # Ensure HttpClient certificate revocation list check is not disabled + +# Azure Storage / Shared Access Signature +dotnet_diagnostic.CA5375.severity = error # Do not use account shared access signature +dotnet_diagnostic.CA5376.severity = error # Use SharedAccessProtocol HttpsOnly +dotnet_diagnostic.CA5377.severity = error # Use container level access policy # XML Security dotnet_diagnostic.CA3061.severity = error # Do not add schema by URL @@ -382,9 +525,15 @@ dotnet_diagnostic.CA3147.severity = error # Mark verb handlers with ValidateAnti dotnet_diagnostic.CA5363.severity = error # Do not disable request validation dotnet_diagnostic.CA5365.severity = error # Do not disable HTTP header checking dotnet_diagnostic.CA5368.severity = error # Set ViewStateUserKey for classes derived from Page +dotnet_diagnostic.CA5382.severity = error # Use secure cookies in ASP.NET Core +dotnet_diagnostic.CA5383.severity = error # Ensure use of secure cookies in ASP.NET Core +dotnet_diagnostic.CA5391.severity = error # Use antiforgery tokens in ASP.NET Core MVC controllers +dotnet_diagnostic.CA5395.severity = error # Miss HttpVerb attribute for action methods +dotnet_diagnostic.CA5396.severity = error # Set HttpOnly to true for HttpCookie # P/Invoke & DLL Security dotnet_diagnostic.CA2101.severity = error # Specify marshalling for P/Invoke string arguments +dotnet_diagnostic.CA5392.severity = error # Use DefaultDllImportSearchPaths attribute for P/Invokes dotnet_diagnostic.CA5393.severity = error # Do not use unsafe DllImportSearchPath value # Archive & File Security @@ -401,79 +550,572 @@ dotnet_diagnostic.CA2153.severity = error # Do not catch corrupted state excepti dotnet_diagnostic.CA5367.severity = error # Do not serialize types with pointer fields ################### -# Microsoft .NET Analyzers (CA) - Reliability Rules (Additional) -################### -dotnet_diagnostic.CA2017.severity = error # Parameter count mismatch in logging -dotnet_diagnostic.CA2018.severity = error # Buffer.BlockCopy count argument -dotnet_diagnostic.CA2019.severity = error # ThreadStatic fields should not use inline initialization -dotnet_diagnostic.CA2020.severity = error # Prevent behavioral change with IntPtr/UIntPtr -dotnet_diagnostic.CA2021.severity = error # Don't call Cast/OfType with incompatible types -dotnet_diagnostic.CA2022.severity = error # Avoid inexact read with Stream.Read -dotnet_diagnostic.CA2023.severity = error # Invalid braces in message template -dotnet_diagnostic.CA2025.severity = error # Do not pass IDisposable into unawaited tasks - -################### -# Roslynator Analyzers (RCS) - Code Simplification +# SonarAnalyzer (Sxxxx) — global suppressions +################### +dotnet_diagnostic.S1075.severity = none # Hardcoded URI — canonical SourceLink hosts are the point +dotnet_diagnostic.S2436.severity = none # Too many generic parameters — needed for the projector overload +dotnet_diagnostic.S4036.severity = none # PATH-relative process spawn — benchmark only, trusted env + +################### +# Microsoft .NET Runtime Obsoletions (SYSLIB0xxx) +################### +dotnet_diagnostic.SYSLIB0001.severity = error # The UTF-7 encoding is insecure and should not be used +dotnet_diagnostic.SYSLIB0002.severity = error # PrincipalPermissionAttribute is not honored by the runtime and must not be used +dotnet_diagnostic.SYSLIB0003.severity = error # Code Access Security (CAS) is not supported or honored by the runtime +dotnet_diagnostic.SYSLIB0004.severity = error # Constrained Execution Region (CER) feature is not supported +dotnet_diagnostic.SYSLIB0005.severity = error # Global Assembly Cache is not supported +dotnet_diagnostic.SYSLIB0006.severity = error # Thread.Abort is not supported and throws PlatformNotSupportedException +dotnet_diagnostic.SYSLIB0007.severity = error # The default implementation of this cryptography algorithm is not supported +dotnet_diagnostic.SYSLIB0008.severity = error # The CreatePdbGenerator API is not supported and throws PlatformNotSupportedException +dotnet_diagnostic.SYSLIB0009.severity = error # The AuthenticationManager Authenticate and PreAuthenticate methods are not supported +dotnet_diagnostic.SYSLIB0010.severity = error # This Remoting API is not supported and throws PlatformNotSupportedException +dotnet_diagnostic.SYSLIB0011.severity = error # BinaryFormatter serialization is obsolete +dotnet_diagnostic.SYSLIB0012.severity = error # Assembly.CodeBase and Assembly.EscapedCodeBase are obsolete +dotnet_diagnostic.SYSLIB0013.severity = error # Uri.EscapeUriString can corrupt the Uri string +dotnet_diagnostic.SYSLIB0014.severity = error # WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete — use HttpClient +dotnet_diagnostic.SYSLIB0015.severity = error # DisablePrivateReflectionAttribute is not honored +dotnet_diagnostic.SYSLIB0016.severity = error # Use Marshal.GetExceptionPointers instead +dotnet_diagnostic.SYSLIB0017.severity = error # Strong name signing is not supported on .NET Core +dotnet_diagnostic.SYSLIB0018.severity = error # ReflectionOnly loading is not supported +dotnet_diagnostic.SYSLIB0019.severity = error # RuntimeEnvironment members SystemConfigurationFile, GetSystemVersion, and FromGlobalAccessCache are not supported +dotnet_diagnostic.SYSLIB0020.severity = error # JsonSerializerOptions.IgnoreNullValues is obsolete — use DefaultIgnoreCondition +dotnet_diagnostic.SYSLIB0021.severity = error # Derived cryptographic types are obsolete — use the Create factory methods +dotnet_diagnostic.SYSLIB0022.severity = error # Rijndael types are obsolete — use Aes +dotnet_diagnostic.SYSLIB0023.severity = error # RNGCryptoServiceProvider is obsolete — use RandomNumberGenerator +dotnet_diagnostic.SYSLIB0024.severity = error # Creating and unloading AppDomains is not supported and throws an exception +dotnet_diagnostic.SYSLIB0025.severity = error # SuppressIldasmAttribute has no effect in .NET 6.0+ +dotnet_diagnostic.SYSLIB0026.severity = error # X509Certificate and X509Certificate2 parameterless constructors are obsolete +dotnet_diagnostic.SYSLIB0027.severity = error # PublicKey.Key is obsolete — use the appropriate method to get the public key +dotnet_diagnostic.SYSLIB0028.severity = error # X509Certificate2.PrivateKey is obsolete — use the appropriate method to get the private key +dotnet_diagnostic.SYSLIB0029.severity = error # ProduceLegacyHmacValues is obsolete +dotnet_diagnostic.SYSLIB0030.severity = error # HMACSHA1 always uses the algorithm implementation provided by the platform +dotnet_diagnostic.SYSLIB0031.severity = error # EncodingProvider.GetEncoding(int, EncoderFallback, DecoderFallback) is obsolete +dotnet_diagnostic.SYSLIB0032.severity = error # Recovery from corrupted process state exceptions is not supported +dotnet_diagnostic.SYSLIB0033.severity = error # Rfc2898DeriveBytes.CryptDeriveKey is obsolete +dotnet_diagnostic.SYSLIB0034.severity = error # CmsSigner has been deprecated — use a constructor that accepts a SubjectIdentifierType +dotnet_diagnostic.SYSLIB0035.severity = error # ComputeCounterSignature without specifying a CmsSigner is obsolete +dotnet_diagnostic.SYSLIB0036.severity = error # Regex.CompileToAssembly is obsolete and not supported — use the RegexGenerator source generator +dotnet_diagnostic.SYSLIB0037.severity = error # AssemblyName members HashAlgorithm, ProcessorArchitecture, and VersionCompatibility are obsolete +dotnet_diagnostic.SYSLIB0038.severity = error # SerializationFormat.Binary is obsolete +dotnet_diagnostic.SYSLIB0039.severity = error # TLS versions 1.0 and 1.1 have known vulnerabilities and are not recommended +dotnet_diagnostic.SYSLIB0040.severity = error # EncryptionPolicy.NoEncryption and AllowEncryption significantly reduce security +dotnet_diagnostic.SYSLIB0041.severity = error # The default iteration count for Rfc2898DeriveBytes parameters is outdated and insecure +dotnet_diagnostic.SYSLIB0042.severity = error # ToXmlString and FromXmlString have no implementation for ECC types +dotnet_diagnostic.SYSLIB0043.severity = error # ECDiffieHellmanPublicKey.ToByteArray() and the associated constructor do not have a consistent and interoperable implementation +dotnet_diagnostic.SYSLIB0044.severity = error # AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are obsolete +dotnet_diagnostic.SYSLIB0045.severity = error # Cryptographic factory methods accepting an algorithm name are obsolete +dotnet_diagnostic.SYSLIB0046.severity = error # ControlledExecution.Run may corrupt the process and should not be used in production code +dotnet_diagnostic.SYSLIB0047.severity = error # XmlSecureResolver is obsolete +dotnet_diagnostic.SYSLIB0048.severity = error # RSA.EncryptValue and RSA.DecryptValue are not supported and throw NotSupportedException +dotnet_diagnostic.SYSLIB0049.severity = error # JsonSerializerOptions.AddContext is obsolete +dotnet_diagnostic.SYSLIB0050.severity = error # Formatter-based serialization (ISerializable, FormatterServices, etc.) is obsolete +dotnet_diagnostic.SYSLIB0051.severity = error # The Exception API ISerializable / GetObjectData formatter-based serialization is obsolete +dotnet_diagnostic.SYSLIB0052.severity = error # Members for assigning to AssemblyName.CodeBase or AssemblyName.EscapedCodeBase are obsolete +dotnet_diagnostic.SYSLIB0053.severity = error # AesGcm should indicate the required tag size for encryption and decryption +dotnet_diagnostic.SYSLIB0054.severity = error # Strong name signing is not supported and throws PlatformNotSupportedException +dotnet_diagnostic.SYSLIB0055.severity = error # 'Memory.Pin' may be incorrectly used with structures +dotnet_diagnostic.SYSLIB0056.severity = error # LoadFromHash and related members are obsolete +dotnet_diagnostic.SYSLIB0057.severity = error # Use X509CertificateLoader instead to load certificates +dotnet_diagnostic.SYSLIB0058.severity = error # KeyExchangeAlgorithm and KeyExchangeStrength are obsolete on SslStream +dotnet_diagnostic.SYSLIB0059.severity = error # SystemEvents.EventsThreadShutdown event is obsolete +dotnet_diagnostic.SYSLIB0060.severity = error # Constructors of DirectoryServices.ActiveDirectory.ConfigurationContext are obsolete +dotnet_diagnostic.SYSLIB0061.severity = error # CryptographyConfig.AddOID and AddAlgorithm methods are obsolete + +################### +# Microsoft .NET Source Generator Diagnostics (SYSLIB1xxx) +################### +# LoggerMessage source generator +dotnet_diagnostic.SYSLIB1001.severity = error # Logging method names cannot start with _ +dotnet_diagnostic.SYSLIB1002.severity = error # Don't include log level parameters as templates in the logging message +dotnet_diagnostic.SYSLIB1003.severity = error # LoggerMessage attribute log level value must be defined +dotnet_diagnostic.SYSLIB1004.severity = error # Logging class cannot be in nested types +dotnet_diagnostic.SYSLIB1005.severity = error # Could not find a required type definition +dotnet_diagnostic.SYSLIB1006.severity = error # Multiple logging methods cannot use the same event ID within a class +dotnet_diagnostic.SYSLIB1007.severity = error # Logging methods must return void +dotnet_diagnostic.SYSLIB1008.severity = error # One of the arguments to a logging method must implement ILogger +dotnet_diagnostic.SYSLIB1009.severity = error # Logging methods must be static +dotnet_diagnostic.SYSLIB1010.severity = error # Logging methods must be partial +dotnet_diagnostic.SYSLIB1011.severity = error # Logging methods cannot be generic +dotnet_diagnostic.SYSLIB1012.severity = error # Logging methods must not have a body +dotnet_diagnostic.SYSLIB1013.severity = error # Missing logger argument to logging method +dotnet_diagnostic.SYSLIB1015.severity = error # Argument is not referenced from the logging message +dotnet_diagnostic.SYSLIB1016.severity = error # Logging methods cannot have a parameter named the same as one of the template parameters +dotnet_diagnostic.SYSLIB1017.severity = error # Logging methods cannot have more than one parameter of type LogLevel +dotnet_diagnostic.SYSLIB1018.severity = error # Logging method parameter doesn't appear in template +dotnet_diagnostic.SYSLIB1019.severity = error # Couldn't find a field of type ILogger +dotnet_diagnostic.SYSLIB1020.severity = error # Found multiple fields of type ILogger +dotnet_diagnostic.SYSLIB1021.severity = error # Cannot have multiple logging methods with the same name +dotnet_diagnostic.SYSLIB1022.severity = error # Logging methods cannot have multiple template parameters with the same name +dotnet_diagnostic.SYSLIB1023.severity = error # Generating more than 6 arguments is not supported +dotnet_diagnostic.SYSLIB1024.severity = error # Logging method template name conflicts with a method parameter name +dotnet_diagnostic.SYSLIB1025.severity = error # Multiple template parameters with the same name +# Skipped SYSLIB1014 (Reserved) + +# System.Text.Json source generator +dotnet_diagnostic.SYSLIB1030.severity = error # JsonSourceGenerator did not generate serialization metadata for type +dotnet_diagnostic.SYSLIB1031.severity = error # Duplicate type registration in JsonSerializerContext +dotnet_diagnostic.SYSLIB1032.severity = error # JsonSerializerContext classes must be partial +dotnet_diagnostic.SYSLIB1033.severity = error # Serializable types must have at least one constructor +dotnet_diagnostic.SYSLIB1034.severity = error # Type uses an unsupported [JsonExtensionData] property +dotnet_diagnostic.SYSLIB1036.severity = error # Init-only properties are not supported in source generation +dotnet_diagnostic.SYSLIB1037.severity = error # Source generator does not support [JsonInclude] on inaccessible members +dotnet_diagnostic.SYSLIB1038.severity = error # JsonInclude on a non-public property is not supported +dotnet_diagnostic.SYSLIB1039.severity = error # Type with extension data of unsupported shape +dotnet_diagnostic.SYSLIB1040.severity = error # Type with cycles is not supported +dotnet_diagnostic.SYSLIB1041.severity = error # Duplicate type names registered with JsonSerializerContext +# Skipped SYSLIB1035 (Reserved) + +# GeneratedRegex source generator +dotnet_diagnostic.SYSLIB1042.severity = error # Use 'GeneratedRegexAttribute' for static regex creation +dotnet_diagnostic.SYSLIB1043.severity = error # The regex pattern is not valid +dotnet_diagnostic.SYSLIB1044.severity = error # The regex source generator failed +dotnet_diagnostic.SYSLIB1045.severity = error # Convert to 'GeneratedRegexAttribute' for compile-time regex generation +dotnet_diagnostic.SYSLIB1046.severity = error # Could not find an applicable Regex constructor +dotnet_diagnostic.SYSLIB1047.severity = error # The regex options are not valid for source generation +dotnet_diagnostic.SYSLIB1048.severity = error # GeneratedRegex source generator encountered an unhandled error +dotnet_diagnostic.SYSLIB1049.severity = error # Multiple regex source generators conflict + +# LibraryImport (P/Invoke) source generator +dotnet_diagnostic.SYSLIB1050.severity = error # Method should be marked 'LibraryImport' instead of 'DllImport' +dotnet_diagnostic.SYSLIB1051.severity = error # Specified type is not supported by source-generated P/Invokes +dotnet_diagnostic.SYSLIB1052.severity = error # Specified configuration is not supported by source-generated P/Invokes +dotnet_diagnostic.SYSLIB1053.severity = error # 'LibraryImportAttribute' requires unsafe code +dotnet_diagnostic.SYSLIB1054.severity = error # Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time +dotnet_diagnostic.SYSLIB1055.severity = error # Invalid 'CustomMarshallerAttribute' usage +dotnet_diagnostic.SYSLIB1056.severity = error # Specified marshaller type is invalid +dotnet_diagnostic.SYSLIB1057.severity = error # Marshaller type does not have the required shape +dotnet_diagnostic.SYSLIB1058.severity = error # Invalid 'NativeMarshallingAttribute' usage +dotnet_diagnostic.SYSLIB1059.severity = error # Marshaller type must be a closed generic type +dotnet_diagnostic.SYSLIB1060.severity = error # Specified native type is invalid +dotnet_diagnostic.SYSLIB1061.severity = error # Marshaller type has incompatible method signatures +dotnet_diagnostic.SYSLIB1062.severity = error # Project must be updated with 'true' +dotnet_diagnostic.SYSLIB1063.severity = error # 'CustomMarshaller' types must have an 'in' or 'ref' first parameter +dotnet_diagnostic.SYSLIB1064.severity = error # Forwarder marshaller types should not have 'GenericPlaceholder' + +# GeneratedComInterface / COM source generator +dotnet_diagnostic.SYSLIB1090.severity = error # Convert to 'GeneratedComInterface' +dotnet_diagnostic.SYSLIB1091.severity = error # Type is an invalid 'GeneratedComInterface' base interface +dotnet_diagnostic.SYSLIB1092.severity = error # 'GeneratedComInterface' must specify 'Guid' +dotnet_diagnostic.SYSLIB1093.severity = error # Method on 'GeneratedComInterface' has unsupported return type +dotnet_diagnostic.SYSLIB1094.severity = error # Method on 'GeneratedComInterface' has unsupported parameter +dotnet_diagnostic.SYSLIB1095.severity = error # Type implements 'GeneratedComInterface' but is not partial +dotnet_diagnostic.SYSLIB1096.severity = error # 'GeneratedComClassAttribute' must be applied to a partial type +dotnet_diagnostic.SYSLIB1097.severity = error # 'GeneratedComClassAttribute' type must implement at least one 'GeneratedComInterface' +dotnet_diagnostic.SYSLIB1098.severity = error # 'GeneratedComInterface' interface inheritance not supported + +# Configuration binding source generator +dotnet_diagnostic.SYSLIB1100.severity = error # Configuration binding source generator: type is not supported +dotnet_diagnostic.SYSLIB1101.severity = error # Configuration binding source generator: property on type is not supported +dotnet_diagnostic.SYSLIB1102.severity = error # Configuration binding source generator: not enabled +dotnet_diagnostic.SYSLIB1103.severity = error # Configuration binding source generator: type lacks a parameterless constructor +dotnet_diagnostic.SYSLIB1104.severity = error # Configuration binding source generator: language version is too low + +################### +# Microsoft .NET Style Rules (IDExxxx) - Language Rules +################### +# EnforceCodeStyleInBuild=true (set in Directory.Build.props) promotes these +# IDE rules into `dotnet build` so they fire at compile time, not just in the +# IDE. We enable the ones that are unambiguous wins and skip the rules that +# conflict with an existing convention (SA1101, SA1200, SA1206, SA1400, SA1633, +# etc.) so we don't double-report. + +# Bug catchers — always error. +dotnet_diagnostic.IDE0035.severity = error # Remove unreachable code +dotnet_diagnostic.IDE0043.severity = error # Format string contains invalid placeholder +dotnet_diagnostic.IDE0051.severity = error # Remove unused private member +dotnet_diagnostic.IDE0052.severity = error # Remove unread private member +dotnet_diagnostic.IDE0076.severity = error # Remove invalid global SuppressMessage attribute +dotnet_diagnostic.IDE0077.severity = error # Avoid legacy format target in global SuppressMessage attribute +dotnet_diagnostic.IDE0080.severity = error # Remove unnecessary suppression operator + +# Simplification / cleanup. +dotnet_diagnostic.IDE0001.severity = error # Simplify name +dotnet_diagnostic.IDE0002.severity = error # Simplify member access +dotnet_diagnostic.IDE0004.severity = none # Remove unnecessary cast — disabled because we deliberately keep explicit casts in Apple platform-specific paths and other reflection/marshalling sites where the analyzer's "redundant" judgement is wrong +dotnet_diagnostic.IDE0005.severity = none # Remove unnecessary import - DUPLICATE S1128 (slower: 2.364s vs 551ms) +dotnet_diagnostic.IDE0016.severity = error # Use throw expression +dotnet_diagnostic.IDE0017.severity = error # Use object initializers +dotnet_diagnostic.IDE0018.severity = error # Inline variable declaration +dotnet_diagnostic.IDE0019.severity = error # Use pattern matching to avoid 'as' followed by a 'null' check +dotnet_diagnostic.IDE0020.severity = error # Use pattern matching to avoid 'is' check followed by a cast +dotnet_diagnostic.IDE0028.severity = error # Use collection initializers +dotnet_diagnostic.IDE0029.severity = none # Use coalesce expression — handled by RCS1128 +dotnet_diagnostic.IDE0030.severity = error # Use coalesce expression (nullable types) +dotnet_diagnostic.IDE0031.severity = none # Use null propagation — handled by RCS1146 +dotnet_diagnostic.IDE0032.severity = none # Use auto property — handled by RCS1085 +dotnet_diagnostic.IDE0033.severity = error # Use explicitly provided tuple name +dotnet_diagnostic.IDE0034.severity = error # Simplify default expression +dotnet_diagnostic.IDE0037.severity = error # Use inferred member name +dotnet_diagnostic.IDE0038.severity = error # Use pattern matching ('is' check without a cast) +dotnet_diagnostic.IDE0041.severity = error # Use 'is null' check +dotnet_diagnostic.IDE0042.severity = error # Deconstruct variable declaration +dotnet_diagnostic.IDE0044.severity = error # Add readonly modifier +dotnet_diagnostic.IDE0046.severity = suggestion # Use conditional expression for return — downgraded because nested coalescing/ternary chains it produces hurt readability +dotnet_diagnostic.IDE0047.severity = error # Remove unnecessary parentheses +dotnet_diagnostic.IDE0049.severity = error # Use language keywords instead of framework type names for type references +dotnet_diagnostic.IDE0054.severity = error # Use compound assignment +dotnet_diagnostic.IDE0056.severity = suggestion # Use index operator +dotnet_diagnostic.IDE0057.severity = suggestion # Use range operator +dotnet_diagnostic.IDE0059.severity = error # Remove unnecessary value assignment +dotnet_diagnostic.IDE0062.severity = error # Make local function static +dotnet_diagnostic.IDE0063.severity = error # Use simple 'using' statement +dotnet_diagnostic.IDE0064.severity = error # Make struct fields writable — flag readonly-but-mutable struct fields where intent diverges from declaration +dotnet_diagnostic.IDE0066.severity = error # Use switch expression +dotnet_diagnostic.IDE0070.severity = error # Use 'System.HashCode.Combine' +dotnet_diagnostic.IDE0071.severity = error # Simplify interpolation +dotnet_diagnostic.IDE0072.severity = suggestion # Add missing cases to switch expression - sometimes we miss switch cases dilberately. +dotnet_diagnostic.IDE0074.severity = error # Use compound assignment +dotnet_diagnostic.IDE0075.severity = error # Simplify conditional expression +dotnet_diagnostic.IDE0078.severity = error # Use pattern matching +dotnet_diagnostic.IDE0082.severity = error # Convert typeof to nameof +dotnet_diagnostic.IDE0083.severity = error # Use pattern matching ('not' operator) +dotnet_diagnostic.IDE0084.severity = error # Use pattern matching ('IsNot' operator) +dotnet_diagnostic.IDE0090.severity = error # Simplify 'new' expression +dotnet_diagnostic.IDE0100.severity = error # Remove unnecessary equality operator +dotnet_diagnostic.IDE0110.severity = error # Remove unnecessary discard +dotnet_diagnostic.IDE0120.severity = error # Simplify LINQ expression +dotnet_diagnostic.IDE0150.severity = error # Prefer 'null' check over type check +dotnet_diagnostic.IDE0161.severity = error # Use file-scoped namespace declaration +dotnet_diagnostic.IDE0170.severity = error # Simplify property pattern +dotnet_diagnostic.IDE0180.severity = error # Use tuple to swap values +dotnet_diagnostic.IDE0200.severity = error # Remove unnecessary lambda expression +dotnet_diagnostic.IDE0220.severity = error # Add explicit cast in foreach loop +dotnet_diagnostic.IDE0240.severity = error # Remove redundant nullable directive +dotnet_diagnostic.IDE0241.severity = error # Remove unnecessary nullable directive +dotnet_diagnostic.IDE0250.severity = error # Make struct 'readonly' +dotnet_diagnostic.IDE0251.severity = error # Make member 'readonly' +dotnet_diagnostic.IDE0260.severity = error # Use pattern matching +dotnet_diagnostic.IDE0270.severity = error # Use coalesce expression + +# Disabled — conflict with an existing SA/CA rule or project convention. +dotnet_diagnostic.IDE0003.severity = none # Remove 'this' qualifier — we don't follow the this. prefix style (SA1101 = none) +dotnet_diagnostic.IDE0007.severity = none # Use var — we don't force var in either direction +dotnet_diagnostic.IDE0008.severity = none # Use explicit type — we don't force var in either direction +dotnet_diagnostic.IDE0009.severity = none # Member access should be qualified — SA1101 = none +dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement — too noisy for default/fallthrough patterns +dotnet_diagnostic.IDE0011.severity = none # Add braces — RCS1001 / RCS1007 already handle this +dotnet_diagnostic.IDE0021.severity = none # Use expression body for constructors — preference not enforced +dotnet_diagnostic.IDE0022.severity = none # Use expression body for methods — preference not enforced +dotnet_diagnostic.IDE0023.severity = none # Use expression body for conversion operators — preference not enforced +dotnet_diagnostic.IDE0024.severity = none # Use expression body for operators — preference not enforced +dotnet_diagnostic.IDE0025.severity = none # Use expression body for properties — preference not enforced +dotnet_diagnostic.IDE0026.severity = none # Use expression body for indexers — preference not enforced +dotnet_diagnostic.IDE0027.severity = none # Use expression body for accessors — preference not enforced +dotnet_diagnostic.IDE0036.severity = none # Order modifiers — SA1206 handles +dotnet_diagnostic.IDE0039.severity = none # Use local function — anonymous method preference not enforced +dotnet_diagnostic.IDE0040.severity = none # Add accessibility modifiers — SA1400 handles +dotnet_diagnostic.IDE0045.severity = none # Use conditional expression for assignment — preference not enforced +dotnet_diagnostic.IDE0048.severity = none # Add parentheses for clarity — preference not enforced +dotnet_diagnostic.IDE0053.severity = none # Use expression body for lambdas — preference not enforced +dotnet_diagnostic.IDE0055.severity = none # Formatting — StyleCop SA rules own formatting +dotnet_diagnostic.IDE0058.severity = none # Remove unnecessary expression value — too noisy (ignores fluent APIs) +dotnet_diagnostic.IDE0060.severity = none # Remove unused parameter — CA1801 handles (with api_surface config) +dotnet_diagnostic.IDE0061.severity = none # Use expression body for local function — preference not enforced +dotnet_diagnostic.IDE0065.severity = none # 'using' directive placement — SA1200 = none (we put usings outside file-scoped namespaces) +dotnet_diagnostic.IDE0073.severity = none # Require file header — SA1633 handles +dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression — can misfire on multi-TFM suppressions +dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure — we don't enforce strict mirror +dotnet_diagnostic.IDE0160.severity = none # Use block-scoped namespace — we use file-scoped (IDE0161) +dotnet_diagnostic.IDE0210.severity = none # Convert to top-level statements — we use Main style +dotnet_diagnostic.IDE0211.severity = none # Convert to 'Program.Main' style — we use Main style +dotnet_diagnostic.IDE0280.severity = none # Use nameof — CA1507 / RCS1015 handle this +dotnet_diagnostic.IDE0290.severity = none # Use primary constructor — subjective, not enforced +dotnet_diagnostic.IDE0300.severity = error # Use collection expression for array +dotnet_diagnostic.IDE0301.severity = error # Use collection expression for empty +dotnet_diagnostic.IDE0302.severity = error # Use collection expression for stackalloc +dotnet_diagnostic.IDE0303.severity = error # Use collection expression for Create +dotnet_diagnostic.IDE0304.severity = error # Use collection expression for builder +dotnet_diagnostic.IDE0305.severity = error # Use collection expression for fluent +dotnet_diagnostic.IDE0306.severity = error # Use collection expression for new +dotnet_diagnostic.IDE0320.severity = error # Make anonymous function static — eliminates the closure-capture display class allocation +dotnet_diagnostic.IDE0050.severity = error # Convert anonymous type to tuple +dotnet_diagnostic.IDE0230.severity = error # Use UTF-8 string literal — better hot-path encoding for byte-array constants +dotnet_diagnostic.IDE0310.severity = none # Convert lambda expression to method group — handled by RCS1207 +dotnet_diagnostic.IDE0370.severity = none # Remove unnecessary suppression (null-forgiving operator) — disabled for the same reason as RCS1249: multi-TFM nullability annotations can differ per platform +dotnet_diagnostic.IDE0380.severity = error # Remove unnecessary unsafe modifier +dotnet_diagnostic.IDE1005.severity = error # Use conditional delegate call + +################### +# Microsoft .NET Style Rules (IDE1xxx / IDE3xxx) - Naming & Miscellaneous +################### +# Naming conventions — SA1300 family already enforces PascalCase / interface +# prefix / field casing (with our _underscore convention on SA1306/1309/1311 +# deliberately disabled). Leaving IDE1006 off because configuring +# `dotnet_naming_rule.*` would duplicate what SA already enforces and could +# conflict with the _underscore convention. +dotnet_diagnostic.IDE1006.severity = none # Naming rule violation — SA1300 family handles naming +dotnet_diagnostic.IDE3000.severity = none # Disabled per project convention — not enforced + +################### +# Roslynator Analyzers (RCS1xxx) - Code Simplification ################### dotnet_diagnostic.RCS1001.severity = error # Add braces (when expression spans over multiple lines) +dotnet_diagnostic.RCS1003.severity = error # Add braces to if-else (when expression spans over multiple lines) dotnet_diagnostic.RCS1005.severity = error # Simplify nested using statement dotnet_diagnostic.RCS1006.severity = error # Merge 'else' with nested 'if' -dotnet_diagnostic.RCS1020.severity = error # Simplify Nullable to T? +dotnet_diagnostic.RCS1007.severity = error # Add braces +dotnet_diagnostic.RCS1031.severity = none # Remove unnecessary braces in switch section -- we don't mind braces in switch statements +dotnet_diagnostic.RCS1032.severity = error # Remove redundant parentheses +dotnet_diagnostic.RCS1033.severity = error # Remove redundant boolean literal +dotnet_diagnostic.RCS1039.severity = error # Remove argument list from attribute +dotnet_diagnostic.RCS1042.severity = error # Remove enum default underlying type +dotnet_diagnostic.RCS1043.severity = error # Remove 'partial' modifier from type with a single part dotnet_diagnostic.RCS1049.severity = error # Simplify boolean comparison +dotnet_diagnostic.RCS1058.severity = error # Use compound assignment +dotnet_diagnostic.RCS1061.severity = error # Merge 'if' with nested 'if' dotnet_diagnostic.RCS1068.severity = error # Simplify logical negation dotnet_diagnostic.RCS1069.severity = error # Remove unnecessary case label +dotnet_diagnostic.RCS1070.severity = error # Remove redundant default switch section dotnet_diagnostic.RCS1071.severity = error # Remove redundant base constructor call dotnet_diagnostic.RCS1073.severity = error # Convert 'if' to 'return' statement dotnet_diagnostic.RCS1074.severity = error # Remove redundant constructor +dotnet_diagnostic.RCS1078.severity = error # Use "" or 'string.Empty' dotnet_diagnostic.RCS1084.severity = error # Use coalesce expression instead of conditional expression +dotnet_diagnostic.RCS1085.severity = error # Use auto-implemented property +dotnet_diagnostic.RCS1089.severity = error # Use --/++ operator instead of assignment +dotnet_diagnostic.RCS1097.severity = error # Remove redundant 'ToString' call +dotnet_diagnostic.RCS1103.severity = error # Convert 'if' to assignment +dotnet_diagnostic.RCS1104.severity = error # Simplify conditional expression +dotnet_diagnostic.RCS1105.severity = error # Unnecessary interpolation +dotnet_diagnostic.RCS1107.severity = error # Remove redundant 'ToCharArray' call +dotnet_diagnostic.RCS1114.severity = error # Remove redundant delegate creation +dotnet_diagnostic.RCS1124.severity = error # Inline local variable +dotnet_diagnostic.RCS1126.severity = error # Add braces to if-else dotnet_diagnostic.RCS1128.severity = error # Use coalesce expression +dotnet_diagnostic.RCS1129.severity = error # Remove redundant field initialization +dotnet_diagnostic.RCS1132.severity = error # Remove redundant overriding member +dotnet_diagnostic.RCS1133.severity = error # Remove redundant Dispose/Close call +dotnet_diagnostic.RCS1134.severity = error # Remove redundant statement dotnet_diagnostic.RCS1143.severity = error # Simplify coalesce expression +dotnet_diagnostic.RCS1145.severity = error # Remove redundant 'as' operator +dotnet_diagnostic.RCS1146.severity = error # Use conditional access +dotnet_diagnostic.RCS1151.severity = error # Remove redundant cast dotnet_diagnostic.RCS1171.severity = error # Simplify lazy initialization dotnet_diagnostic.RCS1173.severity = error # Use coalesce expression instead of 'if' +dotnet_diagnostic.RCS1174.severity = error # Remove redundant async/await +dotnet_diagnostic.RCS1179.severity = error # Unnecessary assignment +dotnet_diagnostic.RCS1180.severity = error # Inline lazy initialization +dotnet_diagnostic.RCS1188.severity = error # Remove redundant auto-property initialization +dotnet_diagnostic.RCS1192.severity = error # Unnecessary usage of verbatim string literal +dotnet_diagnostic.RCS1199.severity = error # Unnecessary null check +dotnet_diagnostic.RCS1206.severity = error # Use conditional access instead of conditional expression +dotnet_diagnostic.RCS1207.severity = error # Use anonymous function or method group +dotnet_diagnostic.RCS1211.severity = error # Remove unnecessary 'else' +dotnet_diagnostic.RCS1212.severity = error # Remove redundant assignment +dotnet_diagnostic.RCS1214.severity = error # Unnecessary interpolated string +dotnet_diagnostic.RCS1216.severity = error # Unnecessary unsafe context +dotnet_diagnostic.RCS1217.severity = error # Convert interpolated string to concatenation +dotnet_diagnostic.RCS1218.severity = error # Simplify code branching +dotnet_diagnostic.RCS1220.severity = error # Use pattern matching instead of combination of 'is' and cast +dotnet_diagnostic.RCS1221.severity = error # Use pattern matching instead of combination of 'as' and null check +dotnet_diagnostic.RCS1238.severity = error # Avoid nested ?: operators +dotnet_diagnostic.RCS1244.severity = error # Simplify 'default' expression +dotnet_diagnostic.RCS1249.severity = none # Unnecessary null-forgiving operator — disabled because multi-TFM nullability annotations can differ per platform, leading to false positives +dotnet_diagnostic.RCS1251.severity = error # Remove unnecessary braces from record declaration dotnet_diagnostic.RCS1259.severity = error # Remove empty syntax (replaces RCS1066) -dotnet_diagnostic.RCS1264.severity = error # Use 'var' or explicit type (replaces RCS1010, RCS1176, RCS1177) +dotnet_diagnostic.RCS1262.severity = error # Unnecessary raw string literal +dotnet_diagnostic.RCS1265.severity = error # Remove redundant catch block +dotnet_diagnostic.RCS1268.severity = error # Simplify numeric comparison ################### -# Roslynator Analyzers (RCS) - Code Quality & Best Practices +# Roslynator Analyzers (RCS1xxx) - Code Quality ################### -dotnet_diagnostic.RCS1018.severity = error # Add/remove accessibility modifiers -dotnet_diagnostic.RCS1037.severity = error # Remove trailing white-space -dotnet_diagnostic.RCS1055.severity = error # Unnecessary semicolon at the end of declaration -dotnet_diagnostic.RCS1078.severity = error # Use "" or 'string.Empty' -dotnet_diagnostic.RCS1085.severity = error # Use auto-implemented property -dotnet_diagnostic.RCS1090.severity = error # Add/remove 'ConfigureAwait(false)' call +dotnet_diagnostic.RCS1013.severity = error # Use predefined type +dotnet_diagnostic.RCS1014.severity = error # Use explicitly/implicitly typed array +dotnet_diagnostic.RCS1015.severity = error # Use nameof operator +dotnet_diagnostic.RCS1016.severity = error # Use block body or expression body +dotnet_diagnostic.RCS1020.severity = error # Simplify Nullable to T? +dotnet_diagnostic.RCS1021.severity = error # Convert lambda expression body to expression body +dotnet_diagnostic.RCS1044.severity = error # Remove original exception from throw statement +dotnet_diagnostic.RCS1046.severity = none # Asynchronous method name should end with 'Async' — TUnit test method naming convention doesn't follow the Async suffix +dotnet_diagnostic.RCS1047.severity = error # Non-asynchronous method name should not end with 'Async' +dotnet_diagnostic.RCS1048.severity = error # Use lambda expression instead of anonymous method +dotnet_diagnostic.RCS1050.severity = error # Include/omit parentheses when creating new object +dotnet_diagnostic.RCS1051.severity = error # Add/remove parentheses from condition in conditional operator +dotnet_diagnostic.RCS1056.severity = none # Avoid usage of using alias directive - used to avoid conflicts +dotnet_diagnostic.RCS1059.severity = error # Avoid locking on publicly accessible instance +dotnet_diagnostic.RCS1075.severity = error # Avoid empty catch clause that catches System.Exception +dotnet_diagnostic.RCS1079.severity = error # Throwing of new NotImplementedException +dotnet_diagnostic.RCS1081.severity = error # Split variable declaration +dotnet_diagnostic.RCS1093.severity = error # File contains no code +dotnet_diagnostic.RCS1094.severity = error # Declare using directive on top level +dotnet_diagnostic.RCS1096.severity = error # Use 'HasFlag' method or bitwise operator +dotnet_diagnostic.RCS1098.severity = error # Constant values should be placed on right side of comparisons +dotnet_diagnostic.RCS1099.severity = error # Default label should be the last label in a switch section dotnet_diagnostic.RCS1102.severity = error # Make class static -dotnet_diagnostic.RCS1105.severity = error # Unnecessary interpolation -dotnet_diagnostic.RCS1138.severity = error # Add summary to documentation comment -dotnet_diagnostic.RCS1139.severity = error # Add summary element to documentation comment -dotnet_diagnostic.RCS1158.severity = none # Static member in generic type should use a type parameter -dotnet_diagnostic.RCS1163.severity = none # Unused parameter +dotnet_diagnostic.RCS1108.severity = error # Add 'static' modifier to all partial class declarations +dotnet_diagnostic.RCS1111.severity = error # Add braces to switch section with multiple statements +dotnet_diagnostic.RCS1113.severity = error # Use 'string.IsNullOrEmpty' method +dotnet_diagnostic.RCS1118.severity = error # Mark local variable as const +dotnet_diagnostic.RCS1123.severity = error # Add parentheses when necessary +dotnet_diagnostic.RCS1130.severity = error # Bitwise operation on enum without Flags attribute +dotnet_diagnostic.RCS1135.severity = error # Declare enum member with zero value (when enum has FlagsAttribute) +dotnet_diagnostic.RCS1136.severity = error # Merge switch sections with equivalent content +dotnet_diagnostic.RCS1154.severity = error # Sort enum members +dotnet_diagnostic.RCS1155.severity = error # Use StringComparison when comparing strings +dotnet_diagnostic.RCS1156.severity = error # Use string.Length instead of comparison with empty string +dotnet_diagnostic.RCS1157.severity = error # Composite enum value contains undefined flag +dotnet_diagnostic.RCS1159.severity = error # Use EventHandler +dotnet_diagnostic.RCS1160.severity = error # Abstract type should not have public constructors +dotnet_diagnostic.RCS1161.severity = none # Enum should declare explicit values - do not need explicit values +dotnet_diagnostic.RCS1162.severity = error # Avoid chain of assignments dotnet_diagnostic.RCS1166.severity = error # Value type object is never equal to null dotnet_diagnostic.RCS1168.severity = suggestion # Parameter name differs from base name -dotnet_diagnostic.RCS1179.severity = error # Unnecessary assignment -dotnet_diagnostic.RCS1180.severity = error # Inline lazy initialization -dotnet_diagnostic.RCS1188.severity = error # Remove redundant auto-property initialization +dotnet_diagnostic.RCS1169.severity = error # Make field read-only +dotnet_diagnostic.RCS1170.severity = error # Use read-only auto-implemented property +dotnet_diagnostic.RCS1172.severity = error # Use 'is' operator instead of 'as' operator +dotnet_diagnostic.RCS1187.severity = error # Use constant instead of field +dotnet_diagnostic.RCS1191.severity = error # Declare enum value as combination of names +dotnet_diagnostic.RCS1193.severity = error # Overriding member should not change 'params' modifier +dotnet_diagnostic.RCS1196.severity = error # Call extension method as instance method +dotnet_diagnostic.RCS1200.severity = error # Call 'Enumerable.ThenBy' instead of 'Enumerable.OrderBy' dotnet_diagnostic.RCS1201.severity = error # Use method chaining -dotnet_diagnostic.RCS1207.severity = error # Use anonymous function or method group -dotnet_diagnostic.RCS1211.severity = error # Remove unnecessary 'else' +dotnet_diagnostic.RCS1202.severity = error # Avoid NullReferenceException +dotnet_diagnostic.RCS1204.severity = error # Use EventArgs.Empty +dotnet_diagnostic.RCS1205.severity = error # Order named arguments according to the order of parameters +dotnet_diagnostic.RCS1208.severity = error # Reduce 'if' nesting +dotnet_diagnostic.RCS1209.severity = error # Order type parameter constraints +dotnet_diagnostic.RCS1210.severity = error # Return completed task instead of returning null +dotnet_diagnostic.RCS1215.severity = error # Expression is always equal to true/false +dotnet_diagnostic.RCS1222.severity = error # Merge preprocessor directives +dotnet_diagnostic.RCS1223.severity = suggestion # Mark publicly visible type with DebuggerDisplay attribute — only data types benefit; the rule is too broad to be an error +dotnet_diagnostic.RCS1224.severity = error # Make method an extension method +dotnet_diagnostic.RCS1225.severity = error # Make class sealed +dotnet_diagnostic.RCS1227.severity = error # Validate arguments correctly +dotnet_diagnostic.RCS1229.severity = error # Use async/await when necessary dotnet_diagnostic.RCS1231.severity = suggestion # Make parameter ref read-only +dotnet_diagnostic.RCS1233.severity = error # Use short-circuiting operator +dotnet_diagnostic.RCS1234.severity = error # Duplicate enum value +dotnet_diagnostic.RCS1239.severity = error # Use 'for' statement instead of 'while' statement +dotnet_diagnostic.RCS1240.severity = error # Operator is unnecessary dotnet_diagnostic.RCS1242.severity = error # Do not pass non-read-only struct by read-only reference +dotnet_diagnostic.RCS1243.severity = error # Duplicate word in a comment +dotnet_diagnostic.RCS1247.severity = error # Fix documentation comment tag dotnet_diagnostic.RCS1248.severity = error # Normalize null check -dotnet_diagnostic.RCS1256.severity = none # Invalid argument null check +dotnet_diagnostic.RCS1250.severity = error # Use implicit/explicit object creation +dotnet_diagnostic.RCS1252.severity = error # Normalize usage of infinite loop +dotnet_diagnostic.RCS1254.severity = error # Normalize format of enum flag value +dotnet_diagnostic.RCS1255.severity = none # Simplify argument null check — conflicts with our ArgumentExceptionHelper helper pattern +dotnet_diagnostic.RCS1257.severity = error # Use enum field explicitly +dotnet_diagnostic.RCS1258.severity = error # Unnecessary enum flag +dotnet_diagnostic.RCS1260.severity = error # Add/remove trailing comma +dotnet_diagnostic.RCS1261.severity = error # Resource can be disposed asynchronously +dotnet_diagnostic.RCS1264.severity = error # Use 'var' or explicit type (replaces RCS1010, RCS1176, RCS1177) +dotnet_diagnostic.RCS1266.severity = error # Use raw string literal +dotnet_diagnostic.RCS1267.severity = error # Use string interpolation instead of 'string.Concat' ################### -# Roslynator Analyzers (RCS) - Performance & Optimization +# Roslynator Analyzers (RCS1xxx) - Performance ################### -dotnet_diagnostic.RCS1058.severity = error # Use compound assignment dotnet_diagnostic.RCS1077.severity = error # Optimize LINQ method call dotnet_diagnostic.RCS1080.severity = error # Use 'Count/Length' property instead of 'Any' method dotnet_diagnostic.RCS1112.severity = error # Combine 'Enumerable.Where' method chain +dotnet_diagnostic.RCS1186.severity = error # Use Regex instance instead of static method dotnet_diagnostic.RCS1190.severity = error # Join string expressions dotnet_diagnostic.RCS1195.severity = error # Use ^ operator dotnet_diagnostic.RCS1197.severity = error # Optimize StringBuilder.Append/AppendLine call -dotnet_diagnostic.RCS1198.severity = none # Avoid unnecessary boxing of value type -dotnet_diagnostic.RCS1214.severity = error # Unnecessary interpolated string +dotnet_diagnostic.RCS1198.severity = none # Avoid unnecessary boxing of value type — boxing is unavoidable bridging Rx and IEnumerable +dotnet_diagnostic.RCS1230.severity = error # Unnecessary explicit use of enumerator dotnet_diagnostic.RCS1235.severity = error # Optimize method call +dotnet_diagnostic.RCS1236.severity = error # Use exception filter +dotnet_diagnostic.RCS1246.severity = error # Use element access + +################### +# Roslynator Analyzers (RCS1xxx) - Maintainability +################### +dotnet_diagnostic.RCS1158.severity = none # Static member in generic type should use a type parameter — common factory pattern +dotnet_diagnostic.RCS1163.severity = none # Unused parameter — interface implementations and Rx selectors often have unused parameters +dotnet_diagnostic.RCS1164.severity = none # Unused type parameter - DUPLICATE IDE0060 (UnusedParameter analyzer 210ms; IDE0060 bundled at lower cost) +dotnet_diagnostic.RCS1165.severity = none # Unconstrained type parameter checked for null - we validate all parameters for non-nullable enabled platforms +dotnet_diagnostic.RCS1182.severity = error # Remove redundant base interface +dotnet_diagnostic.RCS1213.severity = none # Remove unused member declaration - DUPLICATE IDE0051 (slower: 230ms vs 75ms) +dotnet_diagnostic.RCS1241.severity = error # Implement non-generic counterpart +dotnet_diagnostic.RCS1256.severity = none # Invalid argument null check — conflicts with our ArgumentExceptionHelper helper pattern + +################### +# Roslynator Analyzers (RCS1xxx) - Documentation +################### +dotnet_diagnostic.RCS1181.severity = error # Convert comment to documentation comment +dotnet_diagnostic.RCS1189.severity = error # Add or remove region name +dotnet_diagnostic.RCS1226.severity = none # Add paragraph to documentation comment — <para> wrapping is subjective and adds noise +dotnet_diagnostic.RCS1228.severity = error # Unused element in a documentation comment +dotnet_diagnostic.RCS1232.severity = error # Order elements in documentation comment +dotnet_diagnostic.RCS1253.severity = error # Format documentation comment summary +dotnet_diagnostic.RCS1263.severity = error # Invalid reference in a documentation comment + +################### +# Roslynator Analyzers (RCS1xxx) - Disabled (covered by CA/SA equivalent) +################### +dotnet_diagnostic.RCS1018.severity = none # Add/remove accessibility modifiers — covered by SA1400 +dotnet_diagnostic.RCS1019.severity = none # Order modifiers — covered by SA1206 / SA1208 +dotnet_diagnostic.RCS1037.severity = none # Remove trailing white-space — covered by SA1028 +dotnet_diagnostic.RCS1052.severity = none # Declare each attribute separately — covered by SA1133 +dotnet_diagnostic.RCS1055.severity = none # Unnecessary semicolon at the end of declaration — covered by SA1106 +dotnet_diagnostic.RCS1060.severity = none # Declare each type in separate file — covered by SA1402 +dotnet_diagnostic.RCS1090.severity = none # Add/remove 'ConfigureAwait(false)' call — covered by CA2007 (also disabled) +dotnet_diagnostic.RCS1110.severity = none # Declare type inside namespace — covered by CA1050 +dotnet_diagnostic.RCS1138.severity = none # Add summary to documentation comment — covered by SA1600 +dotnet_diagnostic.RCS1139.severity = none # Add summary element to documentation comment — covered by SA1604 +dotnet_diagnostic.RCS1140.severity = none # Add exception to documentation comment — covered by SA1614 +dotnet_diagnostic.RCS1141.severity = none # Add 'param' element to documentation comment — covered by SA1611 +dotnet_diagnostic.RCS1142.severity = none # Add 'typeparam' element to documentation comment — covered by SA1618 +dotnet_diagnostic.RCS1175.severity = none # Unused 'this' parameter — covered by CA1822 +dotnet_diagnostic.RCS1194.severity = none # Implement exception constructors — covered by CA1032 +dotnet_diagnostic.RCS1203.severity = none # Use AttributeUsageAttribute — covered by CA1018 + +################### +# Roslynator Formatting Analyzers (RCS0xxx) - covered by StyleCop SA equivalents +################### +dotnet_diagnostic.RCS0001.severity = none # Add blank line after embedded statement — covered by StyleCop layout rules +dotnet_diagnostic.RCS0002.severity = none # Add blank line after #region — covered by StyleCop SA1517 family +dotnet_diagnostic.RCS0003.severity = none # Add blank line after using directive list — covered by SA1516 +dotnet_diagnostic.RCS0005.severity = none # Add blank line before #endregion — covered by StyleCop layout rules +dotnet_diagnostic.RCS0006.severity = none # Add blank line before using directive list — covered by SA1517 +dotnet_diagnostic.RCS0007.severity = none # Add blank line between accessors — covered by SA1513 +dotnet_diagnostic.RCS0008.severity = none # Add blank line between closing brace and next statement — covered by SA1513 +dotnet_diagnostic.RCS0009.severity = none # Add blank line between declaration and documentation comment — covered by SA1514 +dotnet_diagnostic.RCS0010.severity = none # Add blank line between declarations — covered by SA1516 +dotnet_diagnostic.RCS0011.severity = none # Add/remove blank line between single-line accessors — StyleCop already governs this +dotnet_diagnostic.RCS0012.severity = none # Add blank line between single-line declarations — covered by SA1516 +dotnet_diagnostic.RCS0013.severity = none # Add blank line between single-line declarations of different kind — covered by SA1516 +dotnet_diagnostic.RCS0015.severity = none # Add/remove blank line between using directives — covered by SA1209 / SA1210 +dotnet_diagnostic.RCS0016.severity = none # Put attribute list on its own line — StyleCop SA1133 governs attribute lists +dotnet_diagnostic.RCS0020.severity = none # Format accessor's braces — covered by SA1500 +dotnet_diagnostic.RCS0021.severity = none # Format block's braces — covered by SA1500 +dotnet_diagnostic.RCS0023.severity = none # Format type declaration's braces — covered by SA1500 +dotnet_diagnostic.RCS0024.severity = none # Add new line after switch label — covered by SA1003 +dotnet_diagnostic.RCS0025.severity = none # Put full accessor on its own line — covered by SA1502 +dotnet_diagnostic.RCS0027.severity = none # Place new line after/before binary operator — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0028.severity = none # Place new line after/before '?:' operator — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0029.severity = none # Put constructor initializer on its own line — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0030.severity = none # Put embedded statement on its own line — covered by SA1503 +dotnet_diagnostic.RCS0031.severity = none # Put enum member on its own line — covered by SA1136 +dotnet_diagnostic.RCS0032.severity = none # Place new line after/before arrow token — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0033.severity = none # Put statement on its own line — covered by SA1505 family +dotnet_diagnostic.RCS0034.severity = none # Put type parameter constraint on its own line — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0036.severity = none # Remove blank line between single-line declarations of same kind — covered by SA1516 +dotnet_diagnostic.RCS0039.severity = none # Remove new line before base list — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0041.severity = none # Remove new line between 'if' keyword and 'else' keyword — StyleCop layout rules cover this +dotnet_diagnostic.RCS0042.severity = none # Put auto-accessors on a single line — formatting preference, not enforced +dotnet_diagnostic.RCS0044.severity = none # Use carriage return + linefeed as new line — handled by .gitattributes / editorconfig end_of_line +dotnet_diagnostic.RCS0045.severity = none # Use linefeed as new line — handled by .gitattributes / editorconfig end_of_line +dotnet_diagnostic.RCS0046.severity = none # Use spaces instead of tab — handled by editorconfig indent_style +dotnet_diagnostic.RCS0048.severity = none # Put initializer on a single line — formatting preference, not enforced +dotnet_diagnostic.RCS0049.severity = none # Add blank line after top comment — covered by SA1517 +dotnet_diagnostic.RCS0050.severity = none # Add blank line before top declaration — covered by SA1517 +dotnet_diagnostic.RCS0051.severity = none # Add/remove new line before 'while' in 'do' statement — formatting preference +dotnet_diagnostic.RCS0052.severity = none # Place new line after/before equals token — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0053.severity = none # Fix formatting of a list — formatting preference +dotnet_diagnostic.RCS0054.severity = none # Fix formatting of a call chain — formatting preference +dotnet_diagnostic.RCS0055.severity = none # Fix formatting of a binary expression chain — formatting preference +dotnet_diagnostic.RCS0056.severity = none # A line is too long — line-length not enforced +dotnet_diagnostic.RCS0057.severity = none # Normalize whitespace at the beginning of a file — handled by editorconfig +dotnet_diagnostic.RCS0058.severity = none # Normalize whitespace at the end of a file — covered by SA1518 +dotnet_diagnostic.RCS0059.severity = none # Place new line after/before null-conditional operator — StyleCop wrapping rules cover this +dotnet_diagnostic.RCS0060.severity = none # Add/remove line after file scoped namespace declaration — formatting preference +dotnet_diagnostic.RCS0061.severity = none # Add/remove blank line between switch sections — formatting preference +dotnet_diagnostic.RCS0062.severity = none # Put expression body on its own line — formatting preference +dotnet_diagnostic.RCS0063.severity = none # Remove unnecessary blank line — covered by SA1505 / SA1507 ################### # StyleCop Analyzers (SA) - Spacing Rules @@ -488,7 +1130,7 @@ dotnet_diagnostic.SA1006.severity = error # Preprocessor keywords must not be pr dotnet_diagnostic.SA1007.severity = error # Operator keyword must be followed by space dotnet_diagnostic.SA1008.severity = error # Opening parenthesis must be spaced correctly dotnet_diagnostic.SA1009.severity = error # Closing parenthesis must be spaced correctly -dotnet_diagnostic.SA1010.severity = none # Opening square brackets must be spaced correctly +dotnet_diagnostic.SA1010.severity = none # Opening square brackets must be spaced correctly — conflicts with C# 12 collection expressions dotnet_diagnostic.SA1011.severity = error # Closing square brackets must be spaced correctly dotnet_diagnostic.SA1012.severity = error # Opening braces must be spaced correctly dotnet_diagnostic.SA1013.severity = error # Closing braces must be spaced correctly @@ -512,7 +1154,7 @@ dotnet_diagnostic.SA1028.severity = error # Code must not contain trailing white # StyleCop Analyzers (SA) - Readability Rules ################### dotnet_diagnostic.SA1100.severity = error # Do not prefix calls with base unless local implementation exists -dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this — we don't follow the this. prefix style dotnet_diagnostic.SA1102.severity = error # Query clause must follow previous clause dotnet_diagnostic.SA1103.severity = error # Query clauses must be on same line or separate lines dotnet_diagnostic.SA1104.severity = error # Query clause must begin on new line when previous clause spans multiple lines @@ -530,7 +1172,7 @@ dotnet_diagnostic.SA1116.severity = error # Split parameters must start on line dotnet_diagnostic.SA1117.severity = error # Parameters must be on same line or separate lines dotnet_diagnostic.SA1118.severity = error # Parameter must not span multiple lines dotnet_diagnostic.SA1120.severity = error # Comments must contain text -dotnet_diagnostic.SA1121.severity = error # Use built-in type alias +dotnet_diagnostic.SA1121.severity = none # Use built-in type alias - DUPLICATE RCS1013 (slower: 330ms vs 19ms) dotnet_diagnostic.SA1122.severity = error # Use string.Empty for empty strings dotnet_diagnostic.SA1123.severity = error # Do not place regions within elements dotnet_diagnostic.SA1124.severity = error # Do not use regions @@ -551,7 +1193,7 @@ dotnet_diagnostic.SA1139.severity = error # Use literal suffix notation instead ################### # StyleCop Analyzers (SA) - Ordering Rules ################### -dotnet_diagnostic.SA1200.severity = none # Using directives must be placed correctly +dotnet_diagnostic.SA1200.severity = none # Using directives must be placed correctly — usings live outside file-scoped namespaces dotnet_diagnostic.SA1201.severity = error # Elements must appear in the correct order dotnet_diagnostic.SA1202.severity = error # Elements must be ordered by access dotnet_diagnostic.SA1203.severity = error # Constants must appear before fields @@ -572,20 +1214,20 @@ dotnet_diagnostic.SA1217.severity = error # Using static directives must be orde ################### # StyleCop Analyzers (SA) - Naming Rules ################### -dotnet_diagnostic.SA1300.severity = error # Element must begin with upper-case letter +dotnet_diagnostic.SA1300.severity = none # Element must begin with upper-case letter - DUPLICATE S100/S101 (slower: 101ms vs 47ms) dotnet_diagnostic.SA1302.severity = error # Interface names must begin with I dotnet_diagnostic.SA1303.severity = error # Const field names must begin with upper-case letter dotnet_diagnostic.SA1304.severity = error # Non-private readonly fields must begin with upper-case letter -dotnet_diagnostic.SA1306.severity = none # Field names must begin with lower-case letter +dotnet_diagnostic.SA1306.severity = none # Field names must begin with lower-case letter — we use the _underscore prefix for private fields dotnet_diagnostic.SA1307.severity = error # Accessible fields must begin with upper-case letter dotnet_diagnostic.SA1308.severity = error # Variable names must not be prefixed -dotnet_diagnostic.SA1309.severity = none # Field names must not begin with underscore +dotnet_diagnostic.SA1309.severity = none # Field names must not begin with underscore — we use the _underscore prefix for private fields dotnet_diagnostic.SA1310.severity = error # Field names must not contain underscore -dotnet_diagnostic.SA1311.severity = none # Static readonly fields must begin with upper-case letter +dotnet_diagnostic.SA1311.severity = none # Static readonly fields must begin with upper-case letter — we use the _underscore convention for private static readonly fields too dotnet_diagnostic.SA1312.severity = error # Variable names must begin with lower-case letter dotnet_diagnostic.SA1313.severity = error # Parameter names must begin with lower-case letter dotnet_diagnostic.SA1314.severity = error # Type parameter names must begin with T -dotnet_diagnostic.SA1316.severity = none # Tuple element names should use correct casing +dotnet_diagnostic.SA1316.severity = none # Tuple element names should use correct casing — we don't enforce a tuple element naming convention ################### # StyleCop Analyzers (SA) - Maintainability Rules @@ -602,7 +1244,7 @@ dotnet_diagnostic.SA1407.severity = error # Arithmetic expressions must declare dotnet_diagnostic.SA1408.severity = error # Conditional expressions must declare precedence dotnet_diagnostic.SA1410.severity = error # Remove delegate parenthesis when possible dotnet_diagnostic.SA1411.severity = error # Attribute constructor must not use unnecessary parenthesis -dotnet_diagnostic.SA1413.severity = none # Use trailing commas in multi-line initializers +dotnet_diagnostic.SA1413.severity = none # Use trailing commas in multi-line initializers — trailing commas not required ################### # StyleCop Analyzers (SA) - Layout Rules @@ -610,9 +1252,9 @@ dotnet_diagnostic.SA1413.severity = none # Use trailing commas in multi-line ini dotnet_diagnostic.SA1500.severity = error # Braces for multi-line statements must not share line dotnet_diagnostic.SA1501.severity = error # Statement must not be on single line dotnet_diagnostic.SA1502.severity = error # Element must not be on single line -dotnet_diagnostic.SA1503.severity = error # Braces must not be omitted +dotnet_diagnostic.SA1503.severity = none # Braces must not be omitted - DUPLICATE S121 (slower: 50ms vs 20ms) dotnet_diagnostic.SA1504.severity = error # All accessors must be single-line or multi-line -dotnet_diagnostic.SA1505.severity = none # Opening braces must not be followed by blank line +dotnet_diagnostic.SA1505.severity = error # Opening braces must not be followed by blank line dotnet_diagnostic.SA1506.severity = error # Element documentation headers must not be followed by blank line dotnet_diagnostic.SA1507.severity = error # Code must not contain multiple blank lines in a row dotnet_diagnostic.SA1508.severity = error # Closing braces must not be preceded by blank line @@ -621,7 +1263,7 @@ dotnet_diagnostic.SA1510.severity = error # Chained statement blocks must not be dotnet_diagnostic.SA1511.severity = error # While-do footer must not be preceded by blank line dotnet_diagnostic.SA1512.severity = error # Single-line comments must not be followed by blank line dotnet_diagnostic.SA1513.severity = error # Closing brace must be followed by blank line -dotnet_diagnostic.SA1514.severity = none # Element documentation header must be preceded by blank line +dotnet_diagnostic.SA1514.severity = error # Element documentation header must be preceded by blank line dotnet_diagnostic.SA1515.severity = error # Single-line comment must be preceded by blank line dotnet_diagnostic.SA1516.severity = error # Elements must be separated by blank line dotnet_diagnostic.SA1517.severity = error # Code must not contain blank lines at start of file @@ -663,8 +1305,8 @@ dotnet_diagnostic.SA1633.severity = error # File must have header dotnet_diagnostic.SA1634.severity = error # File header must show copyright dotnet_diagnostic.SA1635.severity = error # File header must have copyright text dotnet_diagnostic.SA1636.severity = error # File header copyright text must match -dotnet_diagnostic.SA1637.severity = none # File header must contain file name -dotnet_diagnostic.SA1638.severity = none # File header file name documentation must match file name +dotnet_diagnostic.SA1637.severity = none # File header must contain file name — our headers don't include the filename +dotnet_diagnostic.SA1638.severity = none # File header file name documentation must match file name — our headers don't include the filename dotnet_diagnostic.SA1640.severity = error # File header must have valid company text dotnet_diagnostic.SA1641.severity = error # File header company name text must match dotnet_diagnostic.SA1642.severity = error # Constructor summary documentation must begin with standard text @@ -677,128 +1319,7 @@ dotnet_diagnostic.SA1651.severity = error # Do not use placeholder elements ################### dotnet_diagnostic.SX1101.severity = error # Do not prefix local members with this dotnet_diagnostic.SX1309.severity = error # Field names must begin with underscore -dotnet_diagnostic.SX1623.severity = none # Property summary documentation must match accessors (alternative) - -############################################# -# NUnit Analyzers -############################################# -[*.{cs,vb}] - -################### -# NUnit Analyzers - Structure Rules (NUnit1001 - NUnit1999) -################### -dotnet_diagnostic.NUnit1001.severity = error # TestCase args must match parameter types -dotnet_diagnostic.NUnit1002.severity = error # TestCaseSource should use nameof -dotnet_diagnostic.NUnit1003.severity = error # TestCase provided too few arguments -dotnet_diagnostic.NUnit1004.severity = error # TestCase provided too many arguments -dotnet_diagnostic.NUnit1005.severity = error # ExpectedResult type must match return type -dotnet_diagnostic.NUnit1006.severity = error # ExpectedResult must not be used on void methods -dotnet_diagnostic.NUnit1007.severity = error # Non-void method but no ExpectedResult provided -dotnet_diagnostic.NUnit1008.severity = error # ParallelScope.Self at assembly level has no effect -dotnet_diagnostic.NUnit1009.severity = error # ParallelScope.Children on non-parameterized test -dotnet_diagnostic.NUnit1010.severity = error # ParallelScope.Fixtures on a test method -dotnet_diagnostic.NUnit1011.severity = error # TestCaseSource member does not exist -dotnet_diagnostic.NUnit1012.severity = error # async test method must have non-void return type -dotnet_diagnostic.NUnit1013.severity = error # async method must use non-generic Task when no result -dotnet_diagnostic.NUnit1014.severity = error # async method must use Task when result expected -dotnet_diagnostic.NUnit1015.severity = error # Source type does not implement I(Async)Enumerable -dotnet_diagnostic.NUnit1016.severity = error # Source type lacks default constructor -dotnet_diagnostic.NUnit1017.severity = error # Specified source is not static -dotnet_diagnostic.NUnit1018.severity = error # TestCaseSource param count mismatch (target method) -dotnet_diagnostic.NUnit1019.severity = error # Source does not return I(Async)Enumerable -dotnet_diagnostic.NUnit1020.severity = error # Parameters provided to field/property source -dotnet_diagnostic.NUnit1021.severity = error # ValueSource should use nameof -dotnet_diagnostic.NUnit1022.severity = error # Specified ValueSource is not static -dotnet_diagnostic.NUnit1023.severity = error # ValueSource cannot supply required parameters -dotnet_diagnostic.NUnit1024.severity = error # ValueSource does not return I(Async)Enumerable -dotnet_diagnostic.NUnit1025.severity = error # ValueSource member does not exist -dotnet_diagnostic.NUnit1026.severity = error # Test or setup/teardown method is not public -dotnet_diagnostic.NUnit1027.severity = error # Test method has parameters but no arguments supplied -dotnet_diagnostic.NUnit1028.severity = error # Non-test method is public -dotnet_diagnostic.NUnit1029.severity = error # TestCaseSource param count mismatch (Test method) -dotnet_diagnostic.NUnit1030.severity = error # TestCaseSource parameter type mismatch (Test method) -dotnet_diagnostic.NUnit1031.severity = error # ValuesAttribute args must match parameter types -dotnet_diagnostic.NUnit1032.severity = error # IDisposable field/property should be disposed in TearDown -dotnet_diagnostic.NUnit1033.severity = error # TestContext.Write methods will be obsolete -dotnet_diagnostic.NUnit1034.severity = error # Base TestFixtures should be abstract -dotnet_diagnostic.NUnit1035.severity = error # Range 'step' parameter cannot be zero -dotnet_diagnostic.NUnit1036.severity = error # Range: from < to when step is positive -dotnet_diagnostic.NUnit1037.severity = error # Range: from > to when step is negative -dotnet_diagnostic.NUnit1038.severity = error # Attribute values' types must match parameter type - -################### -# NUnit Analyzers - Assertion Rules (NUnit2001 - NUnit2999) -################### -dotnet_diagnostic.NUnit2001.severity = error # Prefer Assert.That(..., Is.False) over ClassicAssert.False -dotnet_diagnostic.NUnit2002.severity = error # Prefer Assert.That(..., Is.False) over ClassicAssert.IsFalse -dotnet_diagnostic.NUnit2003.severity = error # Prefer Assert.That(..., Is.True) over ClassicAssert.IsTrue -dotnet_diagnostic.NUnit2004.severity = error # Prefer Assert.That(..., Is.True) over ClassicAssert.True -dotnet_diagnostic.NUnit2005.severity = error # Prefer Is.EqualTo over AreEqual -dotnet_diagnostic.NUnit2006.severity = error # Prefer Is.Not.EqualTo over AreNotEqual -dotnet_diagnostic.NUnit2007.severity = error # Actual value should not be a constant -dotnet_diagnostic.NUnit2008.severity = error # Incorrect IgnoreCase usage -dotnet_diagnostic.NUnit2009.severity = error # Same value used for actual and expected -dotnet_diagnostic.NUnit2010.severity = error # Use EqualConstraint for better messages -dotnet_diagnostic.NUnit2011.severity = error # Use ContainsConstraint for better messages -dotnet_diagnostic.NUnit2012.severity = error # Use StartsWithConstraint for better messages -dotnet_diagnostic.NUnit2013.severity = error # Use EndsWithConstraint for better messages -dotnet_diagnostic.NUnit2014.severity = error # Use SomeItemsConstraint for better messages -dotnet_diagnostic.NUnit2015.severity = error # Prefer Is.SameAs over AreSame -dotnet_diagnostic.NUnit2016.severity = error # Prefer Is.Null over ClassicAssert.Null -dotnet_diagnostic.NUnit2017.severity = error # Prefer Is.Null over ClassicAssert.IsNull -dotnet_diagnostic.NUnit2018.severity = error # Prefer Is.Not.Null over ClassicAssert.NotNull -dotnet_diagnostic.NUnit2019.severity = error # Prefer Is.Not.Null over ClassicAssert.IsNotNull -dotnet_diagnostic.NUnit2020.severity = error # Incompatible types for SameAs constraint -dotnet_diagnostic.NUnit2021.severity = error # Incompatible types for EqualTo constraint -dotnet_diagnostic.NUnit2022.severity = error # Missing property required for constraint -dotnet_diagnostic.NUnit2023.severity = error # Invalid NullConstraint usage -dotnet_diagnostic.NUnit2024.severity = error # Wrong actual type with String constraint -dotnet_diagnostic.NUnit2025.severity = error # Wrong actual type with ContainsConstraint -dotnet_diagnostic.NUnit2026.severity = error # Wrong actual type with SomeItems+EqualConstraint -dotnet_diagnostic.NUnit2027.severity = error # Prefer Is.GreaterThan over ClassicAssert.Greater -dotnet_diagnostic.NUnit2028.severity = error # Prefer Is.GreaterThanOrEqualTo over GreaterOrEqual -dotnet_diagnostic.NUnit2029.severity = error # Prefer Is.LessThan over ClassicAssert.Less -dotnet_diagnostic.NUnit2030.severity = error # Prefer Is.LessThanOrEqualTo over LessOrEqual -dotnet_diagnostic.NUnit2031.severity = error # Prefer Is.Not.SameAs over AreNotSame -dotnet_diagnostic.NUnit2032.severity = error # Prefer Is.Zero over ClassicAssert.Zero -dotnet_diagnostic.NUnit2033.severity = error # Prefer Is.Not.Zero over ClassicAssert.NotZero -dotnet_diagnostic.NUnit2034.severity = error # Prefer Is.NaN over ClassicAssert.IsNaN -dotnet_diagnostic.NUnit2035.severity = error # Prefer Is.Empty over ClassicAssert.IsEmpty -dotnet_diagnostic.NUnit2036.severity = error # Prefer Is.Not.Empty over ClassicAssert.IsNotEmpty -dotnet_diagnostic.NUnit2037.severity = error # Prefer Does.Contain over ClassicAssert.Contains -dotnet_diagnostic.NUnit2038.severity = error # Prefer Is.InstanceOf over ClassicAssert.IsInstanceOf -dotnet_diagnostic.NUnit2039.severity = error # Prefer Is.Not.InstanceOf over ClassicAssert.IsNotInstanceOf -dotnet_diagnostic.NUnit2040.severity = error # Non-reference types for SameAs constraint -dotnet_diagnostic.NUnit2041.severity = error # Incompatible types for comparison constraint -dotnet_diagnostic.NUnit2042.severity = error # Comparison constraint on object -dotnet_diagnostic.NUnit2043.severity = error # Use ComparisonConstraint for better messages -dotnet_diagnostic.NUnit2044.severity = error # Non-delegate actual parameter -dotnet_diagnostic.NUnit2045.severity = error # Use Assert.EnterMultipleScope or Assert.Multiple -dotnet_diagnostic.NUnit2046.severity = error # Use CollectionConstraint for better messages -dotnet_diagnostic.NUnit2047.severity = error # Incompatible types for Within constraint -dotnet_diagnostic.NUnit2048.severity = error # Prefer Assert.That over StringAssert -dotnet_diagnostic.NUnit2049.severity = error # Prefer Assert.That over CollectionAssert -dotnet_diagnostic.NUnit2050.severity = error # NUnit 4 no longer supports string.Format spec -dotnet_diagnostic.NUnit2051.severity = error # Prefer Is.Positive over ClassicAssert.Positive -dotnet_diagnostic.NUnit2052.severity = error # Prefer Is.Negative over ClassicAssert.Negative -dotnet_diagnostic.NUnit2053.severity = error # Prefer Is.AssignableFrom over ClassicAssert.IsAssignableFrom -dotnet_diagnostic.NUnit2054.severity = error # Prefer Is.Not.AssignableFrom over ClassicAssert.IsNotAssignableFrom -dotnet_diagnostic.NUnit2055.severity = error # Prefer Is.InstanceOf over 'is T' expression -dotnet_diagnostic.NUnit2056.severity = error # Prefer Assert.EnterMultipleScope statement over Multiple - -################### -# NUnit Analyzers - Suppressor Rules (NUnit3001 - NUnit3999) -################### -dotnet_diagnostic.NUnit3001.severity = error # Expression checked in NotNull/IsNotNull/Assert.That -dotnet_diagnostic.NUnit3002.severity = error # Field/Property initialized in SetUp/OneTimeSetUp -dotnet_diagnostic.NUnit3003.severity = error # TestFixture instantiated via reflection -dotnet_diagnostic.NUnit3004.severity = error # Field should be disposed in TearDown/OneTimeTearDown - -################### -# NUnit Analyzers - Style Rules (NUnit4001 - NUnit4999) -################### -dotnet_diagnostic.NUnit4001.severity = error # Simplify the Values attribute -dotnet_diagnostic.NUnit4002.severity = error # Use Specific constraint +dotnet_diagnostic.SX1623.severity = none # Property summary documentation must match accessors (alternative) — alternative SX rule, replaced by standard SA1623 ################### # Trimming Analyzer Warnings (IL2001 - IL2123) @@ -932,6 +1453,683 @@ dotnet_diagnostic.IL3055.severity = error # MakeGenericType on non-supported typ dotnet_diagnostic.IL3056.severity = error # MakeGenericMethod on non-supported method requires dynamic code dotnet_diagnostic.IL3057.severity = error # Reflection access to generic parameter requires dynamic code + +################### +# SonarAnalyzer (Sxxxx) - Blocker Bug +################### +dotnet_diagnostic.S1048.severity = error # Finalizers should not throw exceptions +dotnet_diagnostic.S2190.severity = none # Loops and recursions should not be infinite +dotnet_diagnostic.S2275.severity = none # Composite format strings should not lead to unexpected behavior at runtime - DUPLICATE CA2241 +dotnet_diagnostic.S2857.severity = error # SQL keywords should be delimited by whitespace +dotnet_diagnostic.S2930.severity = error # "IDisposables" should be disposed +dotnet_diagnostic.S2931.severity = error # Classes with "IDisposable" members should implement "IDisposable" +dotnet_diagnostic.S3464.severity = error # Type inheritance should not be recursive +dotnet_diagnostic.S3869.severity = error # "SafeHandle.DangerousGetHandle" should not be called +dotnet_diagnostic.S3889.severity = error # "Thread.Resume" and "Thread.Suspend" should not be used +dotnet_diagnostic.S4159.severity = error # Classes should implement their "ExportAttribute" interfaces + +################### +# SonarAnalyzer (Sxxxx) - Critical Bug +################### +dotnet_diagnostic.S2551.severity = error # Shared resources should not be used for locking +dotnet_diagnostic.S2952.severity = error # Classes should "Dispose" of members from the classes' own "Dispose" methods +dotnet_diagnostic.S3449.severity = error # Right operands of shift operators should be integers +dotnet_diagnostic.S4275.severity = error # Getters and setters should access the expected fields +dotnet_diagnostic.S4277.severity = error # "Shared" parts should not be created with "new" +dotnet_diagnostic.S4583.severity = error # Calls to delegate's method "BeginInvoke" should be paired with calls to "EndInvoke" +dotnet_diagnostic.S4586.severity = error # Non-async "Task/Task" methods should not return null +dotnet_diagnostic.S5856.severity = error # Regular expressions should be syntactically valid +dotnet_diagnostic.S6674.severity = error # Log message template should be syntactically correct + +################### +# SonarAnalyzer (Sxxxx) - Major Bug +################### +dotnet_diagnostic.S1244.severity = error # Floating point numbers should not be tested for equality +dotnet_diagnostic.S1656.severity = error # Variables should not be self-assigned +dotnet_diagnostic.S1751.severity = error # Loops with at most one iteration should be refactored +dotnet_diagnostic.S1764.severity = error # Identical expressions should not be used on both sides of operators +dotnet_diagnostic.S1848.severity = error # Objects should not be created to be dropped immediately without being used +dotnet_diagnostic.S1862.severity = error # Related "if/else if" statements should not have the same condition +dotnet_diagnostic.S2114.severity = error # Collections should not be passed as arguments to their own methods +dotnet_diagnostic.S2123.severity = error # Values should not be uselessly incremented +dotnet_diagnostic.S2201.severity = error # Methods without side effects should not have their return values ignored +dotnet_diagnostic.S2225.severity = error # "ToString()" method should not return null +dotnet_diagnostic.S2251.severity = error # A "for" loop update clause should move the counter in the right direction +dotnet_diagnostic.S2252.severity = error # For-loop conditions should be true at least once +dotnet_diagnostic.S2445.severity = error # Blocks should be synchronized on read-only fields +dotnet_diagnostic.S2688.severity = error # "NaN" should not be used in comparisons +dotnet_diagnostic.S2757.severity = error # Non-existent operators like "=+" should not be used +dotnet_diagnostic.S2761.severity = error # Doubled prefix operators "!!" and "~~" should not be used +dotnet_diagnostic.S2995.severity = error # "Object.ReferenceEquals" should not be used for value types +dotnet_diagnostic.S2996.severity = error # "ThreadStatic" fields should not be initialized +dotnet_diagnostic.S2997.severity = error # "IDisposables" created in a "using" statement should not be returned +dotnet_diagnostic.S3005.severity = error # "ThreadStatic" should not be used on non-static fields +dotnet_diagnostic.S3168.severity = error # "async" methods should not return "void" +dotnet_diagnostic.S3172.severity = error # Delegates should not be subtracted +dotnet_diagnostic.S3244.severity = error # Anonymous delegates should not be used to unsubscribe from Events +dotnet_diagnostic.S3249.severity = error # Classes directly extending "object" should not call "base" in "GetHashCode" or "Equals" +dotnet_diagnostic.S3263.severity = error # Static fields should appear in the order they must be initialized +dotnet_diagnostic.S3343.severity = error # Caller information parameters should come at the end of the parameter list +dotnet_diagnostic.S3346.severity = error # Expressions used in "Debug.Assert" should not produce side effects +dotnet_diagnostic.S3453.severity = error # Classes should not have only "private" constructors +dotnet_diagnostic.S3466.severity = error # Optional parameters should be passed to "base" calls +dotnet_diagnostic.S3598.severity = error # One-way "OperationContract" methods should have "void" return type +dotnet_diagnostic.S3603.severity = error # Methods with "Pure" attribute should return a value +dotnet_diagnostic.S3610.severity = error # Nullable type comparison should not be redundant +dotnet_diagnostic.S3903.severity = error # Types should be defined in named namespaces +dotnet_diagnostic.S3923.severity = error # All branches in a conditional structure should not have exactly the same implementation +dotnet_diagnostic.S3926.severity = error # Deserialization methods should be provided for "OptionalField" members +dotnet_diagnostic.S3927.severity = error # Serialization event handlers should be implemented correctly +dotnet_diagnostic.S3981.severity = error # Collection sizes and array length comparisons should make sense +dotnet_diagnostic.S3984.severity = error # Exceptions should not be created without being thrown +dotnet_diagnostic.S4143.severity = error # Collection elements should not be replaced unconditionally +dotnet_diagnostic.S4210.severity = error # Windows Forms entry points should be marked with STAThread +dotnet_diagnostic.S4260.severity = error # "ConstructorArgument" parameters should exist in constructors +dotnet_diagnostic.S4428.severity = error # "PartCreationPolicyAttribute" should be used with "ExportAttribute" +dotnet_diagnostic.S6507.severity = error # Blocks should not be synchronized on local variables +dotnet_diagnostic.S6677.severity = error # Message template placeholders should be unique +dotnet_diagnostic.S6797.severity = error # Blazor query parameter type should be supported +dotnet_diagnostic.S6798.severity = error # [JSInvokable] attribute should only be used on public methods +dotnet_diagnostic.S6800.severity = error # Component parameter type should match the route parameter type constraint +dotnet_diagnostic.S6930.severity = error # Backslash should be avoided in route templates + +################### +# SonarAnalyzer (Sxxxx) - Minor Bug +################### +dotnet_diagnostic.S1206.severity = none # "Equals(Object)" and "GetHashCode()" should be overridden in pairs - DUPLICATE CA2218 +dotnet_diagnostic.S1226.severity = error # Method parameters, caught exceptions and foreach variables' initial values should not be ignored +dotnet_diagnostic.S2183.severity = error # Integral numbers should not be shifted by zero or more than their number of bits-1 +dotnet_diagnostic.S2184.severity = error # Results of integer division should not be assigned to floating point variables +dotnet_diagnostic.S2328.severity = error # "GetHashCode" should not reference mutable fields +dotnet_diagnostic.S2345.severity = error # Flags enumerations should explicitly initialize all their members +dotnet_diagnostic.S2674.severity = error # The length returned from a stream read should be checked +dotnet_diagnostic.S2934.severity = error # Property assignments should not be made for "readonly" fields not constrained to reference types +dotnet_diagnostic.S2955.severity = error # Generic parameters not constrained to reference types should not be compared to "null" +dotnet_diagnostic.S3363.severity = error # Date and time should not be used as a type for primary keys +dotnet_diagnostic.S3397.severity = error # "base.Equals" should not be used to check for reference equality in "Equals" if "base" is not "object" +dotnet_diagnostic.S3456.severity = error # "string.ToCharArray()" and "ReadOnlySpan.ToArray()" should not be called redundantly +dotnet_diagnostic.S3887.severity = error # Mutable, non-private fields should not be "readonly" + +################### +# SonarAnalyzer (Sxxxx) - Blocker Vulnerability +################### +dotnet_diagnostic.S2115.severity = error # A secure password should be used when connecting to a database +dotnet_diagnostic.S2755.severity = error # XML parsers should not be vulnerable to XXE attacks +dotnet_diagnostic.S3884.severity = error # "CoSetProxyBlanket" and "CoInitializeSecurity" should not be used +dotnet_diagnostic.S6418.severity = error # Secrets should not be hard-coded + +################### +# SonarAnalyzer (Sxxxx) - Critical Vulnerability +################### +dotnet_diagnostic.S4423.severity = error # Weak SSL/TLS protocols should not be used +dotnet_diagnostic.S4426.severity = error # Cryptographic keys should be robust +dotnet_diagnostic.S4433.severity = error # LDAP connections should be authenticated +dotnet_diagnostic.S4830.severity = error # Server certificates should be verified during SSL/TLS connections +dotnet_diagnostic.S5344.severity = error # Passwords should not be stored in plaintext or with a fast hashing algorithm +dotnet_diagnostic.S5445.severity = error # Insecure temporary file creation methods should not be used +dotnet_diagnostic.S5542.severity = error # Encryption algorithms should be used with secure mode and padding scheme +dotnet_diagnostic.S5547.severity = error # Cipher algorithms should be robust +dotnet_diagnostic.S5659.severity = error # JWT should be signed and verified with strong cipher algorithms + +################### +# SonarAnalyzer (Sxxxx) - Major Vulnerability +################### +dotnet_diagnostic.S2068.severity = error # Credentials should not be hard-coded +dotnet_diagnostic.S2612.severity = error # File permissions should not be set to world-accessible values +dotnet_diagnostic.S4211.severity = error # Members should not have conflicting transparency annotations +dotnet_diagnostic.S4212.severity = error # Serialization constructors should be secured +dotnet_diagnostic.S6377.severity = error # XML signatures should be validated securely +dotnet_diagnostic.S7039.severity = error # Content Security Policies should be restrictive + +################### +# SonarAnalyzer (Sxxxx) - Blocker Code Smell +################### +dotnet_diagnostic.S1147.severity = error # Exit methods should not be called +dotnet_diagnostic.S1451.severity = none # Track lack of copyright and license headers +dotnet_diagnostic.S2178.severity = error # Short-circuit logic should be used in boolean contexts +dotnet_diagnostic.S2187.severity = error # Test classes should contain at least one test case +dotnet_diagnostic.S2306.severity = error # "async" and "await" should not be used as identifiers +dotnet_diagnostic.S2368.severity = none # Public methods should not have multidimensional array parameters — jagged arrays are the chosen layout for hot-path lookup tables +dotnet_diagnostic.S2387.severity = error # Child class fields should not shadow parent class fields +dotnet_diagnostic.S2437.severity = error # Unnecessary bit operations should not be performed +dotnet_diagnostic.S2699.severity = error # Tests should include assertions +dotnet_diagnostic.S2953.severity = error # Methods named "Dispose" should implement "IDisposable.Dispose" +dotnet_diagnostic.S2970.severity = error # Assertions should be complete +dotnet_diagnostic.S3060.severity = error # "is" should not be used with "this" +dotnet_diagnostic.S3237.severity = error # "value" contextual keyword should be used +dotnet_diagnostic.S3427.severity = error # Method overloads with default parameter values should not overlap +dotnet_diagnostic.S3433.severity = error # Test method signatures should be correct +dotnet_diagnostic.S3443.severity = error # Type should not be examined on "System.Type" instances +dotnet_diagnostic.S3875.severity = error # "operator==" should not be overloaded on reference types +dotnet_diagnostic.S3877.severity = error # Exceptions should not be thrown from unexpected methods +dotnet_diagnostic.S4462.severity = error # Calls to "async" methods should not be blocking +dotnet_diagnostic.S6422.severity = error # Calls to "async" methods should not be blocking in Azure Functions +dotnet_diagnostic.S6424.severity = error # Interfaces for durable entities should satisfy the restrictions + +################### +# SonarAnalyzer (Sxxxx) - Critical Code Smell +################### +dotnet_diagnostic.S1006.severity = error # Method overrides should not change parameter defaults +dotnet_diagnostic.S1067.severity = none # Expressions should not be too complex +dotnet_diagnostic.S1163.severity = error # Exceptions should not be thrown in finally blocks +dotnet_diagnostic.S1186.severity = error # Methods should not be empty +dotnet_diagnostic.S121.severity = error # Control structures should use curly braces (kept over SA1503 — Sonar 20ms vs SA1503 50ms) +dotnet_diagnostic.S1215.severity = none # "GC.Collect" should not be called +dotnet_diagnostic.S126.severity = none # "if ... else if" constructs should end with "else" clauses +dotnet_diagnostic.S131.severity = none # "switch/Select" statements should contain a "default/Case Else" clauses +dotnet_diagnostic.S134.severity = none # Control flow statements "if", "switch", "for", "foreach", "while", "do" and "try" should not be nested too deeply +dotnet_diagnostic.S1541.severity = error # Methods and properties should not be too complex +dotnet_diagnostic.S1699.severity = error # Constructors should only call non-overridable methods +dotnet_diagnostic.S1821.severity = error # "switch" statements should not be nested +dotnet_diagnostic.S1944.severity = error # Invalid casts should be avoided +dotnet_diagnostic.S1994.severity = error # "for" loop increment clauses should modify the loops' counters +dotnet_diagnostic.S2197.severity = error # Modulus results should not be checked for direct equality +dotnet_diagnostic.S2198.severity = error # Unnecessary mathematical comparisons should not be made +dotnet_diagnostic.S2223.severity = none # Non-constant static fields should not be visible - DUPLICATE CA2211 +dotnet_diagnostic.S2290.severity = error # Field-like events should not be virtual +dotnet_diagnostic.S2291.severity = error # Overflow checking should not be disabled for "Enumerable.Sum" +dotnet_diagnostic.S2302.severity = error # "nameof" should be used +dotnet_diagnostic.S2330.severity = error # Array covariance should not be used +dotnet_diagnostic.S2339.severity = error # Public constant members should not be used +dotnet_diagnostic.S2346.severity = none # Flags enumerations zero-value members should be named "None" - DUPLICATE CA1008 +dotnet_diagnostic.S2360.severity = error # Optional parameters should not be used +dotnet_diagnostic.S2365.severity = error # Properties should not make collection or array copies +dotnet_diagnostic.S2479.severity = error # Whitespace and control characters in string literals should be explicit +dotnet_diagnostic.S2692.severity = error # "IndexOf" checks should not be for positive numbers +dotnet_diagnostic.S2696.severity = error # Instance members should not write to "static" fields +dotnet_diagnostic.S2701.severity = error # Literal boolean values should not be used in assertions +dotnet_diagnostic.S3215.severity = error # "interface" instances should not be cast to concrete types +dotnet_diagnostic.S3216.severity = error # "ConfigureAwait(false)" should be used +dotnet_diagnostic.S3217.severity = error # "Explicit" conversions of "foreach" loops should not be used +dotnet_diagnostic.S3218.severity = error # Inner class members should not shadow outer class "static" or type members +dotnet_diagnostic.S3265.severity = error # Non-flags enums should not be used in bitwise operations +dotnet_diagnostic.S3353.severity = error # Unchanged variables should be marked as "const" +dotnet_diagnostic.S3447.severity = error # "[Optional]" should not be used on "ref" or "out" parameters +dotnet_diagnostic.S3451.severity = error # "[DefaultValue]" should not be used when "[DefaultParameterValue]" is meant +dotnet_diagnostic.S3600.severity = error # "params" should not be introduced on overrides +dotnet_diagnostic.S3776.severity = error # Cognitive Complexity of methods should not be too high +dotnet_diagnostic.S3871.severity = error # Exception types should be "public" +dotnet_diagnostic.S3874.severity = none # "out" and "ref" parameters — repo idiom is TryX(..., out T value) +dotnet_diagnostic.S3904.severity = error # Assemblies should have version information +dotnet_diagnostic.S3937.severity = error # Number patterns should be regular +dotnet_diagnostic.S3972.severity = error # Conditionals should start on new lines +dotnet_diagnostic.S3973.severity = error # A conditionally executed single line should be denoted by indentation +dotnet_diagnostic.S3998.severity = error # Threads should not lock on objects with weak identity +dotnet_diagnostic.S4000.severity = error # Pointers to unmanaged memory should not be visible +dotnet_diagnostic.S4015.severity = error # Inherited member visibility should not be decreased +dotnet_diagnostic.S4019.severity = error # Base class methods should not be hidden +dotnet_diagnostic.S4025.severity = error # Child class fields should not differ from parent class fields only by capitalization +dotnet_diagnostic.S4039.severity = none # Interface methods should be callable by derived types - DUPLICATE CA1033 +dotnet_diagnostic.S4487.severity = none # Unread "private" fields should be removed +dotnet_diagnostic.S4524.severity = error # "default" clauses should be first or last +dotnet_diagnostic.S4635.severity = error # Start index should be used instead of calling Substring +dotnet_diagnostic.S5034.severity = error # "ValueTask" should be consumed correctly +dotnet_diagnostic.S6967.severity = error # ModelState.IsValid should be called in controller actions +dotnet_diagnostic.S8367.severity = error # Identifiers should not conflict with the C# 14 "field" contextual keyword +dotnet_diagnostic.S8368.severity = error # Identifiers should not conflict with the C# 14 "extension" contextual keyword +dotnet_diagnostic.S8380.severity = error # Return types named "partial" should be escaped with "@" +dotnet_diagnostic.S8381.severity = error # "scoped" should be escaped when used as an identifier or type name in parenthesized lambda parameter lists +dotnet_diagnostic.S927.severity = none # Parameter names should match base declaration and other partial definitions - DUPLICATE CA1725 + +################### +# SonarAnalyzer (Sxxxx) - Major Code Smell +################### +dotnet_diagnostic.S103.severity = error # Lines should not be too long +dotnet_diagnostic.S104.severity = error # Files should not have too many lines of code +dotnet_diagnostic.S106.severity = error # Standard outputs should not be used directly to log anything +dotnet_diagnostic.S1066.severity = error # Mergeable "if" statements should be combined +dotnet_diagnostic.S107.severity = error # Methods should not have too many parameters +dotnet_diagnostic.S108.severity = error # Nested blocks of code should not be left empty +dotnet_diagnostic.S109.severity = error # Magic numbers should not be used +dotnet_diagnostic.S110.severity = error # Inheritance tree of classes should not be too deep +dotnet_diagnostic.S1110.severity = error # Redundant pairs of parentheses should be removed +dotnet_diagnostic.S1117.severity = error # Local variables should not shadow class fields or properties +dotnet_diagnostic.S1118.severity = none # Utility classes should not have public constructors - DUPLICATE CA1052 +dotnet_diagnostic.S112.severity = error # General or reserved exceptions should never be thrown +dotnet_diagnostic.S1121.severity = error # Assignments should not be made from within sub-expressions +dotnet_diagnostic.S1123.severity = error # "Obsolete" attributes should include explanations +dotnet_diagnostic.S1134.severity = error # Track uses of "FIXME" tags +dotnet_diagnostic.S1144.severity = none # Unused private types or members should be removed - DUPLICATE IDE0051 +dotnet_diagnostic.S1151.severity = error # "switch case" clauses should not have too many lines of code +dotnet_diagnostic.S1168.severity = error # Empty arrays and collections should be returned instead of null +dotnet_diagnostic.S1172.severity = error # Unused method parameters should be removed +dotnet_diagnostic.S1200.severity = none # Classes should not be coupled to too many other classes +dotnet_diagnostic.S122.severity = error # Statements should be on separate lines +dotnet_diagnostic.S125.severity = error # Sections of code should not be commented out +dotnet_diagnostic.S127.severity = error # "for" loop stop conditions should be invariant +dotnet_diagnostic.S138.severity = error # Functions should not have too many lines of code +dotnet_diagnostic.S1479.severity = error # "switch" statements with many "case" clauses should have only one statement +dotnet_diagnostic.S1607.severity = error # Tests should not be ignored +dotnet_diagnostic.S1696.severity = error # NullReferenceException should not be caught +dotnet_diagnostic.S1854.severity = none # Unused assignments should be removed - DUPLICATE IDE0059 +dotnet_diagnostic.S1871.severity = error # Two branches in a conditional structure should not have exactly the same implementation +dotnet_diagnostic.S2139.severity = error # Exceptions should be either logged or rethrown but not both +dotnet_diagnostic.S2166.severity = none # Classes named like "Exception" should extend "Exception" or a subclass - DUPLICATE CA1710 +dotnet_diagnostic.S2234.severity = error # Arguments should be passed in the same order as the method parameters +dotnet_diagnostic.S2326.severity = error # Unused type parameters should be removed +dotnet_diagnostic.S2327.severity = error # "try" statements with identical "catch" and/or "finally" blocks should be merged +dotnet_diagnostic.S2357.severity = error # Fields should be private +dotnet_diagnostic.S2372.severity = error # Exceptions should not be thrown from property getters +dotnet_diagnostic.S2376.severity = error # Write-only properties should not be used +dotnet_diagnostic.S2629.severity = error # Logging templates should be constant +dotnet_diagnostic.S2681.severity = error # Multiline blocks should be enclosed in curly braces +dotnet_diagnostic.S2743.severity = none # Static fields should not be used in generic types - DUPLICATE CA1000 +dotnet_diagnostic.S2925.severity = error # "Thread.Sleep" should not be used in tests +dotnet_diagnostic.S2933.severity = none # Fields that are only assigned in the constructor should be "readonly" - DUPLICATE IDE0044 +dotnet_diagnostic.S2971.severity = error # LINQ expressions should be simplified +dotnet_diagnostic.S3010.severity = error # Static fields should not be updated in constructors +dotnet_diagnostic.S3011.severity = error # Reflection should not be used to increase accessibility of classes, methods, or fields +dotnet_diagnostic.S3059.severity = none # Types should not have members with visibility set higher than the type's visibility +dotnet_diagnostic.S3063.severity = error # "StringBuilder" data should be used +dotnet_diagnostic.S3169.severity = error # Multiple "OrderBy" calls should not be used +dotnet_diagnostic.S3246.severity = error # Generic type parameters should be co/contravariant when possible +dotnet_diagnostic.S3262.severity = error # "params" should be used on overrides +dotnet_diagnostic.S3264.severity = error # Events should be invoked +dotnet_diagnostic.S3358.severity = error # Ternary operators should not be nested +dotnet_diagnostic.S3366.severity = error # "this" should not be exposed from constructors +dotnet_diagnostic.S3415.severity = error # Assertion arguments should be passed in the correct order +dotnet_diagnostic.S3431.severity = error # "[ExpectedException]" should not be used +dotnet_diagnostic.S3442.severity = error # "abstract" classes should not have "public" constructors +dotnet_diagnostic.S3445.severity = error # Exceptions should not be explicitly rethrown +dotnet_diagnostic.S3457.severity = error # Composite format strings should be used correctly +dotnet_diagnostic.S3597.severity = error # "ServiceContract" and "OperationContract" attributes should be used together +dotnet_diagnostic.S3880.severity = error # Finalizers should not be empty +dotnet_diagnostic.S3881.severity = error # "IDisposable" should be implemented correctly +dotnet_diagnostic.S3885.severity = error # "Assembly.Load" should be used +dotnet_diagnostic.S3898.severity = error # Value types should implement "IEquatable" +dotnet_diagnostic.S3902.severity = error # "Assembly.GetExecutingAssembly" should not be called +dotnet_diagnostic.S3906.severity = error # Event Handlers should have the correct signature +dotnet_diagnostic.S3908.severity = error # Generic event handlers should be used +dotnet_diagnostic.S3909.severity = error # Collections should implement the generic interface +dotnet_diagnostic.S3925.severity = none # "ISerializable" should be implemented correctly - BinaryFormatter / ISerializable serialization is obsoleted (SYSLIB0050/0051) in modern .NET; we do not opt into legacy serialization for any exception type +dotnet_diagnostic.S3928.severity = none # Parameter names used into ArgumentException constructors should match an existing one - DUPLICATE CA2208 +dotnet_diagnostic.S3956.severity = none # "Generic.List" instances should not be part of public APIs +dotnet_diagnostic.S3971.severity = error # "GC.SuppressFinalize" should not be called +dotnet_diagnostic.S3990.severity = error # Assemblies should be marked as CLS compliant +dotnet_diagnostic.S3992.severity = error # Assemblies should explicitly specify COM visibility +dotnet_diagnostic.S3993.severity = error # Custom attributes should be marked with "System.AttributeUsageAttribute" +dotnet_diagnostic.S3994.severity = none # URI Parameters should not be strings +dotnet_diagnostic.S3995.severity = none # URI return values should not be strings +dotnet_diagnostic.S3996.severity = none # URI properties should not be strings +dotnet_diagnostic.S3997.severity = error # String URI overloads should call "System.Uri" overloads +dotnet_diagnostic.S4002.severity = error # Disposable types should declare finalizers +dotnet_diagnostic.S4004.severity = error # Collection properties should be readonly +dotnet_diagnostic.S4005.severity = none # "System.Uri" arguments should be used instead of strings +dotnet_diagnostic.S4016.severity = error # Enumeration members should not be named "Reserved" +dotnet_diagnostic.S4017.severity = none # Method signatures should not contain nested generic types +dotnet_diagnostic.S4035.severity = error # Classes implementing "IEquatable" should be sealed +dotnet_diagnostic.S4050.severity = error # Operators should be overloaded consistently +dotnet_diagnostic.S4055.severity = none # Literals should not be passed as localized parameters +dotnet_diagnostic.S4057.severity = error # Locales should be set for data types +dotnet_diagnostic.S4059.severity = none # Property names should not match get methods - DUPLICATE CA1721 +dotnet_diagnostic.S4070.severity = error # Non-flags enums should not be marked with "FlagsAttribute" +dotnet_diagnostic.S4144.severity = error # Methods should not have identical implementations +dotnet_diagnostic.S4200.severity = error # Native methods should be wrapped +dotnet_diagnostic.S4214.severity = none # "P/Invoke" methods should not be visible - DUPLICATE CA1401 +dotnet_diagnostic.S4220.severity = error # Events should have proper arguments +dotnet_diagnostic.S4456.severity = error # Parameter validation in yielding methods should be wrapped +dotnet_diagnostic.S4457.severity = none # Parameter validation in "async"/"await" methods should be wrapped +dotnet_diagnostic.S4545.severity = error # "DebuggerDisplayAttribute" strings should reference existing members +dotnet_diagnostic.S4581.severity = error # "new Guid()" should not be used +dotnet_diagnostic.S6354.severity = error # Use a testable date/time provider +dotnet_diagnostic.S6419.severity = error # Azure Functions should be stateless +dotnet_diagnostic.S6420.severity = error # Client instances should not be recreated on each Azure Function invocation +dotnet_diagnostic.S6421.severity = error # Azure Functions should use Structured Error Handling +dotnet_diagnostic.S6423.severity = error # Azure Functions should log all failures +dotnet_diagnostic.S6561.severity = error # Avoid using "DateTime.Now" for benchmarking or timing operations +dotnet_diagnostic.S6562.severity = error # Always set the "DateTimeKind" when creating new "DateTime" instances +dotnet_diagnostic.S6563.severity = error # Use UTC when recording DateTime instants +dotnet_diagnostic.S6566.severity = error # Use "DateTimeOffset" instead of "DateTime" +dotnet_diagnostic.S6575.severity = error # Use "TimeZoneInfo.FindSystemTimeZoneById" without converting the timezones with "TimezoneConverter" +dotnet_diagnostic.S6580.severity = none # Use a format provider when parsing date and time - DUPLICATE CA1305 +dotnet_diagnostic.S6673.severity = error # Log message template placeholders should be in the right order +dotnet_diagnostic.S6802.severity = error # Using lambda expressions in loops should be avoided in Blazor markup section +dotnet_diagnostic.S6803.severity = error # Parameters with SupplyParameterFromQuery attribute should be used only in routable components +dotnet_diagnostic.S6931.severity = error # ASP.NET controller actions should not have a route template starting with "/" +dotnet_diagnostic.S6932.severity = error # Use model binding instead of reading raw request data +dotnet_diagnostic.S6934.severity = error # A Route attribute should be added to the controller when a route template is specified at the action level +dotnet_diagnostic.S6960.severity = error # Controllers should not have mixed responsibilities +dotnet_diagnostic.S6961.severity = error # API Controllers should derive from ControllerBase instead of Controller +dotnet_diagnostic.S6962.severity = error # You should pool HTTP connections with HttpClientFactory +dotnet_diagnostic.S6964.severity = error # Value type property used as input in a controller action should be nullable, required or annotated with the JsonRequiredAttribute to avoid under-posting. +dotnet_diagnostic.S6965.severity = error # REST API actions should be annotated with an HTTP verb attribute +dotnet_diagnostic.S6966.severity = error # Awaitable method should be used +dotnet_diagnostic.S6968.severity = error # Actions that return a value should be annotated with ProducesResponseTypeAttribute containing the return type +dotnet_diagnostic.S881.severity = error # Increment (++) and decrement (--) operators should not be used in a method call or mixed with other operators in an expression +dotnet_diagnostic.S907.severity = error # "goto" statement should not be used + +################### +# SonarAnalyzer (Sxxxx) - Minor Code Smell +################### +dotnet_diagnostic.S100.severity = error # Methods and properties should be named in PascalCase (kept over SA1300 — S100+S101 47ms vs SA1300 101ms) +dotnet_diagnostic.S101.severity = error # Types should be named in PascalCase (kept over SA1300 — see S100) +dotnet_diagnostic.S105.severity = error # Tabulation characters should not be used +dotnet_diagnostic.S1104.severity = none # Fields should not have public accessibility - DUPLICATE CA1051 +dotnet_diagnostic.S1109.severity = error # A close curly brace should be located at the beginning of a line +dotnet_diagnostic.S1116.severity = none # Empty statements should be removed - DUPLICATE SA1106 +dotnet_diagnostic.S1125.severity = error # Boolean literals should not be redundant +dotnet_diagnostic.S1128.severity = error # Unnecessary "using" should be removed (kept over IDE0005 — Sonar 551ms vs IDE0005 2.364s) +dotnet_diagnostic.S113.severity = none # Files should end with a newline +dotnet_diagnostic.S1155.severity = error # "Any()" should be used to test for emptiness +dotnet_diagnostic.S1185.severity = none # Overriding members should do more than simply call the same member in the base class - DUPLICATE RCS1132 +dotnet_diagnostic.S1192.severity = error # String literals should not be duplicated +dotnet_diagnostic.S1199.severity = error # Nested code blocks should not be used +dotnet_diagnostic.S1210.severity = none # "Equals" and the comparison operators should be overridden when implementing "IComparable" - DUPLICATE CA1036 +dotnet_diagnostic.S1227.severity = none # break statements should not be used except for switch cases +dotnet_diagnostic.S1264.severity = error # A "while" loop should be used instead of a "for" loop +dotnet_diagnostic.S1301.severity = none # "switch" statements should have at least 3 "case" clauses +dotnet_diagnostic.S1312.severity = none # Logger fields should be "private static readonly" +dotnet_diagnostic.S1449.severity = error # Culture should be specified for "string" operations +dotnet_diagnostic.S1450.severity = error # Private fields only used as local variables in methods should become local variables +dotnet_diagnostic.S1481.severity = error # Unused local variables should be removed +dotnet_diagnostic.S1643.severity = error # Strings should not be concatenated using '+' in a loop +dotnet_diagnostic.S1659.severity = error # Multiple variables should not be declared on the same line +dotnet_diagnostic.S1694.severity = error # An abstract class should have both abstract and concrete methods +dotnet_diagnostic.S1698.severity = error # "==" should not be used when "Equals" is overridden +dotnet_diagnostic.S1858.severity = error # "ToString()" calls should not be redundant +dotnet_diagnostic.S1905.severity = none # Redundant casts should not be used - DUPLICATE IDE0004 +dotnet_diagnostic.S1939.severity = error # Inheritance list should not be redundant +dotnet_diagnostic.S1940.severity = error # Boolean checks should not be inverted +dotnet_diagnostic.S2094.severity = error # Classes should not be empty +dotnet_diagnostic.S2148.severity = error # Underscores should be used to make large numbers readable +dotnet_diagnostic.S2156.severity = error # "sealed" classes should not have "protected" members +dotnet_diagnostic.S2219.severity = error # Runtime type checking should be simplified +dotnet_diagnostic.S2221.severity = none # "Exception" should not be caught +dotnet_diagnostic.S2292.severity = error # Trivial properties should be auto-implemented +dotnet_diagnostic.S2325.severity = none # Methods and properties that don't access instance data should be static - DUPLICATE CA1822 +dotnet_diagnostic.S2333.severity = error # Redundant modifiers should not be used +dotnet_diagnostic.S2342.severity = error # Enumeration types should comply with a naming convention +dotnet_diagnostic.S2344.severity = none # Enumeration type names should not have "Flags" or "Enum" suffixes - DUPLICATE CA1711 +dotnet_diagnostic.S2386.severity = error # Mutable fields should not be "public static" +dotnet_diagnostic.S2486.severity = error # Generic exceptions should not be ignored +dotnet_diagnostic.S2737.severity = error # "catch" clauses should do more than rethrow +dotnet_diagnostic.S2760.severity = error # Sequential tests should not check the same condition +dotnet_diagnostic.S3052.severity = error # Members should not be initialized to default values +dotnet_diagnostic.S3220.severity = error # Method calls should not resolve ambiguously to overloads with "params" +dotnet_diagnostic.S3234.severity = error # "GC.SuppressFinalize" should not be invoked for types without destructors +dotnet_diagnostic.S3235.severity = error # Redundant parentheses should not be used +dotnet_diagnostic.S3236.severity = error # Caller information arguments should not be provided explicitly +dotnet_diagnostic.S3240.severity = error # The simplest possible condition syntax should be used +dotnet_diagnostic.S3241.severity = error # Methods should not return values that are never used +dotnet_diagnostic.S3242.severity = none # Method parameters should be declared with base types +dotnet_diagnostic.S3247.severity = error # Duplicate casts should not be made +dotnet_diagnostic.S3251.severity = error # Implementations should be provided for "partial" methods +dotnet_diagnostic.S3253.severity = error # Constructor and destructor declarations should not be redundant +dotnet_diagnostic.S3254.severity = error # Default parameter values should not be passed as arguments +dotnet_diagnostic.S3256.severity = error # "string.IsNullOrEmpty" should be used +dotnet_diagnostic.S3257.severity = error # Declarations and initializations should be as concise as possible +dotnet_diagnostic.S3260.severity = error # Non-derived "private" classes and records should be "sealed" +dotnet_diagnostic.S3261.severity = error # Namespaces should not be empty +dotnet_diagnostic.S3267.severity = none # Loops should be simplified with "LINQ" expressions +dotnet_diagnostic.S3376.severity = none # Attribute, EventArgs, and Exception type names should end with the type being extended - DUPLICATE CA1710 +dotnet_diagnostic.S3398.severity = error # "private" methods called only by inner classes should be moved to those classes +dotnet_diagnostic.S3400.severity = error # Methods should not return constants +dotnet_diagnostic.S3416.severity = error # Loggers should be named for their enclosing types +dotnet_diagnostic.S3440.severity = error # Variables should not be checked against the values they're about to be assigned +dotnet_diagnostic.S3441.severity = error # Redundant property names should be omitted in anonymous classes +dotnet_diagnostic.S3444.severity = error # Interfaces should not simply inherit from base interfaces with colliding members +dotnet_diagnostic.S3450.severity = error # Parameters with "[DefaultParameterValue]" attributes should also be marked "[Optional]" +dotnet_diagnostic.S3458.severity = error # Empty "case" clauses that fall through to the "default" should be omitted +dotnet_diagnostic.S3459.severity = error # Unassigned members should be removed +dotnet_diagnostic.S3532.severity = error # Empty "default" clauses should be removed +dotnet_diagnostic.S3604.severity = error # Member initializer values should not be redundant +dotnet_diagnostic.S3626.severity = none # Jump statements should not be redundant +dotnet_diagnostic.S3717.severity = error # Track use of "NotImplementedException" +dotnet_diagnostic.S3872.severity = error # Parameter names should not duplicate the names of their methods +dotnet_diagnostic.S3876.severity = error # Strings or integral types should be used for indexers +dotnet_diagnostic.S3878.severity = error # Arrays should not be created for params parameters +dotnet_diagnostic.S3897.severity = error # Classes that provide "Equals()" should implement "IEquatable" +dotnet_diagnostic.S3962.severity = none # "static readonly" constants should be "const" instead - DUPLICATE CA1802 +dotnet_diagnostic.S3963.severity = none # "static" fields should be initialized inline - DUPLICATE CA1810 +dotnet_diagnostic.S3967.severity = none # Multidimensional arrays should not be used - DUPLICATE CA1814 +dotnet_diagnostic.S4018.severity = error # All type parameters should be used in the parameter list to enable type inference +dotnet_diagnostic.S4022.severity = error # Enumerations should have "Int32" storage +dotnet_diagnostic.S4023.severity = none # Interfaces should not be empty +dotnet_diagnostic.S4026.severity = error # Assemblies should be marked with "NeutralResourcesLanguageAttribute" +dotnet_diagnostic.S4027.severity = error # Exceptions should provide standard constructors +dotnet_diagnostic.S4040.severity = none # Strings should be normalized to uppercase - DUPLICATE CA1308 +dotnet_diagnostic.S4041.severity = none # Type names should not match namespaces - DUPLICATE CA1724 +dotnet_diagnostic.S4047.severity = error # Generics should be used when appropriate +dotnet_diagnostic.S4049.severity = none # Properties should be preferred - DUPLICATE CA1024 +dotnet_diagnostic.S4052.severity = error # Types should not extend outdated base types +dotnet_diagnostic.S4056.severity = none # Overloads with a "CultureInfo" or an "IFormatProvider" parameter should be used - DUPLICATE CA1305 +dotnet_diagnostic.S4058.severity = error # Overloads with a "StringComparison" parameter should be used +dotnet_diagnostic.S4060.severity = none # Non-abstract attributes should be sealed - DUPLICATE CA1813 +dotnet_diagnostic.S4061.severity = error # "params" should be used instead of "varargs" +dotnet_diagnostic.S4069.severity = none # Operator overloads should have named alternatives - DUPLICATE CA2225 +dotnet_diagnostic.S4136.severity = error # Method overloads should be grouped together +dotnet_diagnostic.S4201.severity = error # Null checks should not be combined with "is" operator checks +dotnet_diagnostic.S4225.severity = error # Extension methods should not extend "object" +dotnet_diagnostic.S4226.severity = none # Extensions should be in separate namespaces +dotnet_diagnostic.S4261.severity = none # Methods should be named according to their synchronicities - Async suffix not used +dotnet_diagnostic.S4663.severity = error # Comments should not be empty +dotnet_diagnostic.S6513.severity = none # "ExcludeFromCodeCoverage" attributes should include a justification - not available on net462 and older TFMs +dotnet_diagnostic.S6585.severity = error # Don't hardcode the format when turning dates and times to strings +dotnet_diagnostic.S6588.severity = error # Use the "UnixEpoch" field instead of creating "DateTime" instances that point to the beginning of the Unix epoch +dotnet_diagnostic.S6602.severity = none # "Find" method should be used instead of the "FirstOrDefault" extension - DUPLICATE CA1826 +dotnet_diagnostic.S6603.severity = error # The collection-specific "TrueForAll" method should be used instead of the "All" extension +dotnet_diagnostic.S6605.severity = error # Collection-specific "Exists" method should be used instead of the "Any" extension +dotnet_diagnostic.S6607.severity = error # The collection should be filtered before sorting by using "Where" before "OrderBy" +dotnet_diagnostic.S6608.severity = none # Prefer indexing instead of "Enumerable" methods on types implementing "IList" - DUPLICATE CA1826 +dotnet_diagnostic.S6609.severity = error # "Min/Max" properties of "Set" types should be used instead of the "Enumerable" extension methods +dotnet_diagnostic.S6610.severity = error # "StartsWith" and "EndsWith" overloads that take a "char" should be used instead of the ones that take a "string" +dotnet_diagnostic.S6612.severity = error # The lambda parameter should be used instead of capturing arguments in "ConcurrentDictionary" methods +dotnet_diagnostic.S6613.severity = error # "First" and "Last" properties of "LinkedList" should be used instead of the "First()" and "Last()" extension methods +dotnet_diagnostic.S6617.severity = error # "Contains" should be used instead of "Any" for simple equality checks +dotnet_diagnostic.S6618.severity = error # "string.Create" should be used instead of "FormattableString" +dotnet_diagnostic.S6664.severity = error # The code block contains too many logging calls +dotnet_diagnostic.S6667.severity = error # Logging in a catch clause should pass the caught exception as a parameter. +dotnet_diagnostic.S6668.severity = error # Logging arguments should be passed to the correct parameter +dotnet_diagnostic.S6669.severity = error # Logger field or property name should comply with a naming convention +dotnet_diagnostic.S6670.severity = error # "Trace.Write" and "Trace.WriteLine" should not be used +dotnet_diagnostic.S6672.severity = error # Generic logger injection should match enclosing type +dotnet_diagnostic.S6675.severity = error # "Trace.WriteLineIf" should not be used with "TraceSwitch" levels +dotnet_diagnostic.S6678.severity = error # Use PascalCase for named placeholders +dotnet_diagnostic.S818.severity = error # Literal suffixes should be upper case + +################### +# SonarAnalyzer (Sxxxx) - Info Code Smell +################### +dotnet_diagnostic.S1133.severity = error # Deprecated code should be removed +dotnet_diagnostic.S1135.severity = error # Track uses of "TODO" tags +dotnet_diagnostic.S1309.severity = none # Track uses of in-source issue suppressions + +################### +# SonarAnalyzer (Sxxxx) - Uncategorized +################### +dotnet_diagnostic.S9999-cpd.severity = error # Copy-paste token calculator +dotnet_diagnostic.S9999-log.severity = error # Log generator +dotnet_diagnostic.S9999-metadata.severity = error # File metadata generator +dotnet_diagnostic.S9999-metrics.severity = error # Metrics calculator +dotnet_diagnostic.S9999-symbolRef.severity = error # Symbol reference calculator +dotnet_diagnostic.S9999-telemetry.severity = error # Telemetry generator +dotnet_diagnostic.S9999-testMethodDeclaration.severity = error # Test method declarations generator +dotnet_diagnostic.S9999-token-type.severity = error # Token type calculator +dotnet_diagnostic.S9999-warning.severity = error # Analysis Warning generator + +################### +# SonarAnalyzer (Sxxxx) - Critical Security Hotspot +################### +dotnet_diagnostic.S2245.severity = none # Using pseudorandom number generators (PRNGs) is security-sensitive - DUPLICATE CA5394 +dotnet_diagnostic.S2257.severity = error # Using non-standard cryptographic algorithms is security-sensitive +dotnet_diagnostic.S4502.severity = error # Disabling CSRF protections is security-sensitive +dotnet_diagnostic.S4790.severity = error # Using weak hashing algorithms is security-sensitive +dotnet_diagnostic.S4792.severity = error # Configuring loggers is security-sensitive +dotnet_diagnostic.S5042.severity = error # Expanding archive files without controlling resource consumption is security-sensitive +dotnet_diagnostic.S5332.severity = error # Using clear-text protocols is security-sensitive +dotnet_diagnostic.S5443.severity = error # Using publicly writable directories is security-sensitive + +################### +# SonarAnalyzer (Sxxxx) - Major Security Hotspot +################### +dotnet_diagnostic.S1313.severity = error # Using hardcoded IP addresses is security-sensitive +dotnet_diagnostic.S2077.severity = error # Formatting SQL queries is security-sensitive +dotnet_diagnostic.S5693.severity = error # Allowing requests with excessive content length is security-sensitive +dotnet_diagnostic.S5753.severity = error # Disabling ASP.NET "Request Validation" feature is security-sensitive +dotnet_diagnostic.S5766.severity = error # Creating Serializable objects without data validation checks is security-sensitive +dotnet_diagnostic.S6444.severity = error # Not specifying a timeout for regular expressions is security-sensitive +dotnet_diagnostic.S6640.severity = error # Using unsafe code blocks is security-sensitive + +################### +# SonarAnalyzer (Sxxxx) - Minor Security Hotspot +################### +dotnet_diagnostic.S2092.severity = error # Creating cookies without the "secure" flag is security-sensitive +dotnet_diagnostic.S3330.severity = error # Creating cookies without the "HttpOnly" flag is security-sensitive +dotnet_diagnostic.S4507.severity = error # Delivering code in production with debug features activated is security-sensitive +dotnet_diagnostic.S5122.severity = error # Having a permissive Cross-Origin Resource Sharing policy is security-sensitive + +############################################# +# JetBrains ReSharper / Rider Inspections +############################################# +# Mirrors the CA / SA / RCS choices above for Rider and ReSharper users. Only +# inspections with a clear mapping to our Roslyn analyzer rules are listed; +# every other ReSharper inspection keeps its default severity. The goal is to +# avoid Rider double-reporting things the Roslyn analyzers already catch and +# to keep IDE severities in sync with CI-enforced severities. + +################### +# ReSharper - Layout / Formatting (covered by StyleCop SA) +################### +resharper_redundant_linebreak_highlighting = none # covered by SA layout rules +resharper_missing_linebreak_highlighting = none # covered by SA layout rules +resharper_bad_empty_braces_line_breaks_highlighting = none # covered by SA1500 +resharper_missing_indent_highlighting = none # covered by editorconfig indent_size +resharper_missing_blank_lines_highlighting = none # covered by SA1516 / SA1517 +resharper_wrong_indent_size_highlighting = none # covered by editorconfig indent_size +resharper_bad_indent_highlighting = none # covered by editorconfig indent_size +resharper_bad_expression_braces_line_breaks_highlighting = none # covered by SA layout rules +resharper_multiple_spaces_highlighting = none # covered by SA1025 +resharper_bad_expression_braces_indent_highlighting = none # covered by SA layout rules +resharper_bad_control_braces_indent_highlighting = none # covered by SA layout rules +resharper_bad_preprocessor_indent_highlighting = none # covered by SA layout rules +resharper_redundant_blank_lines_highlighting = none # covered by SA1516 / SA1507 +resharper_multiple_statements_on_one_line_highlighting = none # covered by SA1119 / SA1503 +resharper_bad_braces_spaces_highlighting = none # covered by SA1012 / SA1013 +resharper_outdent_is_off_prev_level_highlighting = none # editorconfig indentation +resharper_bad_symbol_spaces_highlighting = none # covered by SA spacing rules +resharper_bad_colon_spaces_highlighting = none # covered by SA1024 +resharper_bad_semicolon_spaces_highlighting = none # covered by SA1002 +resharper_bad_square_brackets_spaces_highlighting = none # covered by SA1010 / SA1011 +resharper_bad_parens_spaces_highlighting = none # covered by SA1008 / SA1009 + +################### +# ReSharper - 'this' qualifier (matches SA1101 = none) +################### +resharper_redundant_this_qualifier_highlighting = none # we don't follow the this. prefix style +resharper_arrange_this_qualifier_highlighting = none # we don't follow the this. prefix style +resharper_arrange_default_access_modifier_highlighting = none # SA1400 requires explicit access modifiers — don't suggest removing them +resharper_redundant_default_access_modifier_highlighting = none # SA1400 requires explicit access modifiers — don't suggest removing them +resharper_redundant_access_modifier_highlighting = none # access modifiers are deliberate and SA1400 requires them +resharper_arrange_type_member_modifiers_highlighting = none # SA1206 handles modifier ordering; don't duplicate +resharper_redundant_empty_switch_section_highlighting = none # handled by RCS1070 + +################### +# ReSharper - Redundancies / cleanup +################### +resharper_redundant_using_directive_highlighting = error # IDE0005 may not fire at compile time reliably — keep Rider nagging +resharper_redundant_qualifier_highlighting = error # IDE0001 may not fire at compile time reliably — keep Rider nagging +resharper_redundant_name_qualifier_highlighting = error # IDE0001 may not fire at compile time reliably — keep Rider nagging +resharper_redundant_base_qualifier_highlighting = none # IDE0009 is disabled (SA1101 = none) — don't nag in Rider either +resharper_redundant_cast_highlighting = none # handled by RCS1151 +resharper_redundant_explicit_array_creation_highlighting = none # handled by RCS1014 +resharper_redundant_empty_object_creation_argument_list_highlighting = none # handled by RCS1039 +resharper_redundant_lambda_signature_parentheses_highlighting = error # IDE0063 may not fire at compile time reliably — keep Rider nagging +resharper_redundant_default_member_initializer_highlighting = none # handled by RCS1188 +resharper_redundant_field_initializer_highlighting = none # handled by RCS1129 +resharper_redundant_overriding_member_highlighting = none # handled by RCS1132 +resharper_redundant_verbatim_string_prefix_highlighting = none # handled by RCS1192 +resharper_redundant_string_interpolation_highlighting = none # handled by RCS1105 / RCS1214 +resharper_redundant_to_string_call_highlighting = none # handled by RCS1097 + +################### +# ReSharper - Naming (matches SA/CA naming rules) +################### +resharper_inconsistent_naming_highlighting = error # partially handled by SA1300 family but IDE1006 isn't enabled; keep ReSharper active + +################### +# ReSharper - Unused code (matches CA1801 / CA1823 / IDE0051-0052) +################### +resharper_unused_parameter_local_highlighting = none # handled by CA1801 (private/internal surface) +resharper_unused_parameter_global_highlighting = none # public-API parameters can be unused for compat +resharper_unused_member_local_highlighting = error # IDE0051 may not fire at compile time reliably — keep Rider nagging +resharper_unused_member_global_highlighting = suggestion # public-API members may be intentionally exposed +resharper_unused_field_local_highlighting = none # handled by CA1823 +resharper_unused_field_global_highlighting = none # public-API fields may be intentionally exposed +resharper_unused_type_local_highlighting = error # IDE0052 may not fire at compile time reliably — keep Rider nagging +resharper_unused_type_global_highlighting = suggestion # public-API types may be intentionally exposed +resharper_unused_variable_highlighting = none # handled by CS0219 +resharper_unused_auto_property_accessor_local_highlighting = none # data-class setters are intentional even when unused locally +resharper_unused_auto_property_accessor_global_highlighting = none # public auto-properties may be intentionally exposed + +################### +# ReSharper - Code quality (matches CA/RCS semantic analyzers) +################### +resharper_class_never_instantiated_local_highlighting = none # handled by CA1812 +resharper_class_never_instantiated_global_highlighting = suggestion # public types may be instantiated externally +resharper_class_cannot_be_instantiated_highlighting = none # handled by CA1052 +resharper_class_can_be_sealed_local_highlighting = none # handled by CA1852 (private/internal surface) +resharper_class_can_be_sealed_global_highlighting = none # public types stay open for inheritance +resharper_use_nameof_expression_highlighting = none # handled by CA1507 / RCS1015 +resharper_possible_null_reference_exception_highlighting = none # handled bynullability analyzer +resharper_possible_invalid_operation_exception_highlighting = none # handled bynullability analyzer +resharper_possible_multiple_enumeration_highlighting = none # handled by CA1851 +resharper_possible_mistaken_argument_null_check_highlighting = none # handled by CA1062 +resharper_convert_to_constant_local_highlighting = none # handled by RCS1118 +resharper_convert_to_static_class_highlighting = none # handled by CA1052 +resharper_convert_to_auto_property_highlighting = none # handled by RCS1085 +resharper_convert_to_auto_property_when_possible_highlighting = none # handled by RCS1085 +resharper_convert_to_auto_property_with_private_setter_highlighting = none # handled by RCS1085 +resharper_auto_property_can_be_made_get_only_local_highlighting = none # handled by RCS1170 +resharper_auto_property_can_be_made_get_only_global_highlighting = suggestion # public setters kept for API consumers +resharper_member_can_be_made_static_local_highlighting = none # handled by CA1822 (private/internal surface) +resharper_member_can_be_made_static_global_highlighting = none # public members kept virtual for inheritance +resharper_member_can_be_private_local_highlighting = none # access modifiers are deliberate — don't suggest tightening +resharper_method_has_async_overload_highlighting = none # sync call sites are intentional (e.g. test setup, disposal, sample code) +resharper_member_can_be_private_global_highlighting = none # access modifiers are deliberate — don't suggest tightening +resharper_field_can_be_made_read_only_local_highlighting = none # handled by RCS1169 +resharper_field_can_be_made_read_only_global_highlighting = none # handled by RCS1169 +resharper_merge_into_pattern_highlighting = none # handled by RCS1220 +resharper_merge_conditional_expression_highlighting = none # handled by RCS1173 +resharper_merge_sequential_checks_highlighting = none # handled by RCS1206 +resharper_simplify_conditional_ternary_expression_highlighting = none # handled by RCS1049 / RCS1104 +resharper_simplify_linq_expression_highlighting = error # handled by RCS1077 +resharper_string_compare_to_is_culture_specific_highlighting = none # handled by CA1310 +resharper_string_ends_with_is_culture_specific_highlighting = none # handled by CA1310 +resharper_string_index_of_is_culture_specific_1_highlighting = none # handled by CA1310 +resharper_string_index_of_is_culture_specific_2_highlighting = none # handled by CA1310 +resharper_string_index_of_is_culture_specific_3_highlighting = none # handled by CA1310 +resharper_string_last_index_of_is_culture_specific_1_highlighting = none # handled by CA1310 +resharper_string_last_index_of_is_culture_specific_2_highlighting = none # handled by CA1310 +resharper_string_last_index_of_is_culture_specific_3_highlighting = none # handled by CA1310 +resharper_string_starts_with_is_culture_specific_highlighting = none # handled by CA1310 +resharper_specify_a_culture_in_string_conversion_explicitly_highlighting = none # handled by CA1304 + +################### +# ReSharper - Control flow / nesting +################### +resharper_invert_if_highlighting = suggestion # related to RCS1208 — Rider is more aggressive +resharper_tail_recursive_call_highlighting = suggestion + +################### +# ReSharper - Documentation (covered by StyleCop SA1600 family) +################### +resharper_missing_xml_doc_highlighting = none # covered by SA1600 family +resharper_missing_xml_doc_global_highlighting = none # covered by SA1600 family +resharper_invalid_xml_doc_comment_highlighting = none # handled by SA1612 / SA1613 + +################### +# ReSharper - Typo / spelling (not enforced) +################### +resharper_comment_typo_highlighting = none # typo checks are out of scope +resharper_string_literal_typo_highlighting = none # typo checks are out of scope +resharper_identifier_typo_highlighting = none # typo checks are out of scope +resharper_markup_text_typo_highlighting = none # typo checks are out of scope + +############################################# +# Other Settings +############################################# +vsspell_dictionary_languages = en-US + ############################################# # C++ Files ############################################# @@ -962,8 +2160,3 @@ end_of_line = lf [*.{cmd, bat}] end_of_line = crlf - -############################################# -# Other Settings -############################################# -vsspell_dictionary_languages = en-US diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 896515cbd0..1cec5be88e 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -21,5 +21,7 @@ jobs: configuration: Release productNamespacePrefix: "ReactiveUI" installWorkloads: true + versioningTool: minver + minverMinimumMajorMinor: '23.2' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a9408a783..054d4ac9a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,8 @@ jobs: with: solutionFile: reactiveui.slnx installWorkloads: true + versioningTool: minver + minverMinimumMajorMinor: '23.2' secrets: ES_USERNAME: ${{ secrets.ES_USERNAME }} ES_PASSWORD: ${{ secrets.ES_PASSWORD }} diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 0000000000..775065719d --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,35 @@ +name: SonarCloud + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + sonarcloud: + uses: reactiveui/actions-common/.github/workflows/workflow-common-sonarcloud.yml@main + with: + productNamespacePrefix: ReactiveUI + installWorkloads: true + versioningTool: minver + minverMinimumMajorMinor: '23.2' + sonarProjectKey: reactiveui_ReactiveUI + sonarOrganization: reactiveui + sonarExclusions: '**/tests/**,**/integrationtests/**,**/Benchmarks/**,**/TestResults/**' + sonarCoverageExclusions: '**/tests/**,**/integrationtests/**,**/Benchmarks/**,**/*Tests/**,**/*Tests.cs,**/Generated/**' + # WhenAny{Value,Change}Sink.Arity{N}.cs, WhenAnyMixin.Arity{N}.cs and + # WhenAnyObservableMixin.Arity{N}.cs are template-style arity-specific files: the + # residual duplication is the irreducible per-arity typed surface (N typed fields, + # N constructor params, N switch arms). Compressing further would need source + # generators or boxing-based erasure; suppressing CPD here keeps the metric honest + # while every other Sonar rule still runs on them. Sonar path matchers only support + # *, **, ? — use ? twice to cover 1- and 2-digit arities. + sonarCpdExclusions: '**/tests/**,**/integrationtests/**,**/Benchmarks/**,**/*.Arity?.cs,**/*.Arity??.cs' + sonarTestExclusions: '**/tests/**,**/integrationtests/**,**/Benchmarks/**' + testTimeout: '15m' + secrets: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md index c37cbd2a88..e5eb81f270 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,384 @@ # CLAUDE.md -Follow the canonical repository agent guidance in @agent.md. +This file is the single source of truth for AI/agent assistance in this repository (Claude Code, GitHub Copilot, and other coding agents). It consolidates build/test commands, architecture context, coding standards, and AOT guidance. -If anything in this file conflicts with @agent.md, follow @agent.md. +If there is any conflict between other agent instruction files and this file, follow **CLAUDE.md**. + +--- + +## Repository Orientation + +- **Repository root** +- **Primary working directory for build/test:** `./src` +- **Main solution:** `src/reactiveui.slnx` +- **Benchmarks solution:** `Benchmarks/ReactiveUI.Benchmarks.sln` +- **Integration tests:** `integrationtests/` (platform-specific solutions; not required for most tasks) + +### Full Clone Required + +**CRITICAL:** Use a full, recursive clone. Shallow clones can fail because build/versioning relies on git history. If a clone has already been done you must use the unshallow commit command in git. + +```bash +git clone --recursive https://github.com/reactiveui/reactiveui.git +```` + +--- + +## Solution Format: SLNX + +This repository uses **SLNX** (XML-based solution format) instead of legacy `.sln`. + +* Introduced in Visual Studio 2022 17.10+ +* Rider 2024.1+ support +* Works with `dotnet build/test` the same way `.sln` does +* Main file: `src/reactiveui.slnx` + +--- + +## Build Environment Requirements + +### Required SDKs + +* .NET **8.0**, **9.0**, **10.0** SDKs (all required) + +### Workload Restore (Required) + +**CRITICAL:** Platform workloads must be restored or the build will fail. Run from the `./src` directory. + +```powershell +dotnet --info + +cd src +dotnet workload restore +cd .. +``` + +### Restore & Build + +**CRITICAL:** Run build/test commands from `./src` unless the command explicitly uses `src/`-prefixed paths. + +```powershell +cd src + +dotnet restore reactiveui.slnx + +dotnet build reactiveui.slnx -c Release +dotnet build reactiveui.slnx -c Release -warnaserror + +dotnet clean reactiveui.slnx +``` + +### Windows Requirements + +Building the full solution requires **Windows** due to Windows-only target frameworks (WPF, WinUI, .NET Framework). Non-Windows builds may fail; this is expected. In non-Windows environments, focus on documentation, targeted library changes, or analysis that does not require full compilation. + +--- + +## Testing: Microsoft Testing Platform (MTP) + TUnit + +This repo uses **Microsoft Testing Platform (MTP)** with **TUnit**. This differs from VSTest. + +* MTP is configured via `global.json` +* Additional test settings in `testconfig.json` +* Test projects enable `TestingPlatformDotnetTestSupport` in `Directory.Build.props` + +**Key rule:** TUnit/MTP arguments go **after** `--`. + +### Testing Best Practices + +* **Do NOT use `--no-build`**. Always build before testing to avoid stale binaries. +* To see test output, use `--output Detailed` **before** `--`. +* Repository configuration runs tests **non-parallel** (`"parallel": false` in `testconfig.json`) to avoid interference. + +### Test Commands (run from `./src`) + +```powershell +cd src + +# Run all tests +dotnet test --solution reactiveui.slnx -c Release + +# Run tests for a specific project +dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj + +# Run with code coverage (Microsoft Code Coverage) +dotnet test --solution reactiveui.slnx --coverage --coverage-output-format cobertura + +# Detailed output (place BEFORE --) +dotnet test --solution reactiveui.slnx -- --output Detailed +dotnet test --solution reactiveui.slnx --coverage --coverage-output-format cobertura -- --report-trx --output Detailed + +# List tests +dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --list-tests + +# Fail fast +dotnet test --solution reactiveui.slnx -- --fail-fast + +# Limit parallelism if needed (even though repo defaults non-parallel) +dotnet test --solution reactiveui.slnx -- --maximum-parallel-tests 4 +``` + +### TUnit `--treenode-filter` Syntax + +Pattern: `/{AssemblyName}/{Namespace}/{ClassName}/{TestMethodName}` + +Examples: + +```powershell +# Single test +dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --treenode-filter "/*/*/*/MyTestMethod" + +# All tests in class +dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --treenode-filter "/*/*/MyClassName/*" + +# All tests in namespace +dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --treenode-filter "/*/MyNamespace/*/*" + +# Filter by property (e.g., Category) +dotnet test --solution reactiveui.slnx -- --treenode-filter "/*/*/*/*[Category=Integration]" +``` + +See: [https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test?tabs=dotnet-test-with-mtp](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test?tabs=dotnet-test-with-mtp) +TUnit flags reference: [https://tunit.dev/docs/reference/command-line-flags](https://tunit.dev/docs/reference/command-line-flags) + +--- + +## Key Configuration Files + +* `src/global.json` — sets `"Microsoft.Testing.Platform"` runner +* `src/testconfig.json` — test execution settings (parallel false, coverage format, etc.) +* `src/Directory.Build.props` — repository-wide build configuration (incl. `TestingPlatformDotnetTestSupport`) +* `.github/copilot-instructions.md` — may exist, but should defer to this `agent.md` + +--- + +## Architecture Overview + +ReactiveUI is a cross-platform MVVM framework built on Rx.NET and functional reactive programming principles. + +### Core Library (`src/ReactiveUI/`) + +* `ReactiveObject/` — reactive `INotifyPropertyChanged` base +* `ReactiveCommand/` — observable command pipelines +* `Activation/` — view/viewmodel activation lifecycle +* `Bindings/` — one-way/two-way binding infrastructure +* `Expression/` — expression tree analysis for observation (`WhenAnyValue`) +* `Routing/` — navigation/routing +* `Interactions/` — request/response patterns +* `Builder/` — DI and service registration patterns + +### Platform Extensions + +Examples: + +* `ReactiveUI.Wpf/`, `ReactiveUI.WinUI/`, `ReactiveUI.Maui/`, `ReactiveUI.AndroidX/`, + `ReactiveUI.Blazor/`, `ReactiveUI.Winforms/`, `ReactiveUI.Testing/`, etc. + +### Scheduler Abstraction + +* Prefer `RxSchedulers` (AOT-friendly, avoids reflection/AOT attribute propagation) +* Use `RxApp` only when required (e.g., unit test scheduler detection) + +See `docs/RxSchedulers.md`. + +--- + +## AOT Guidance (Critical) + +This repository targets net8.0+ and supports AOT/trimming scenarios. + +### Primary Rule: Avoid Reflection Paths + +Prefer strongly-typed and source-generator-friendly approaches. Avoid reflection-heavy patterns that require trimming/AOT attributes. + +### Attributes: Use Only If Necessary + +* Avoid introducing DAC/RDC/RUC attributes unless required. +* If an attribute is required, apply it directly (no `#if NET6_0_OR_GREATER` guards). Polyfills are available. + +Example (only when truly needed): + +```csharp +private static object CreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type type) +{ + return Activator.CreateInstance(type)!; +} +``` + +### Suppressions: Last Resort Only — Never Without Human Approval + +**Do NOT add any suppression without explicit human approval.** This covers `[SuppressMessage]`, `[UnconditionalSuppressMessage]`, `#pragma warning`, and `.editorconfig` severity changes. A suppression is a true last resort, used only when a warning genuinely cannot be resolved by fixing the code without harming the design. + +Before suppressing anything: + +1. **Fix the underlying issue first.** Most analyzer warnings indicate a real fix. For example, `S3398` ("method should be moved") means *move the method into the type that uses it* — never suppress it. `SA****` (StyleCop) must always be fixed, never suppressed. +2. **If you believe a suppression is genuinely unavoidable, stop and ask the human.** Present the specific analyzer ID, why it cannot be fixed in code, and the proposed justification. Wait for explicit approval. +3. **Only after approval**, apply it with minimal scope, the specific ID, and a clear `Justification`. + +--- + +## Code Style & Quality Requirements + +**CRITICAL:** Follow ReactiveUI contribution guidelines: +[https://www.reactiveui.net/contribute/index.html](https://www.reactiveui.net/contribute/index.html) + +### Enforced Tooling + +* `.editorconfig` formatting/naming conventions +* StyleCop analyzers (build fails on violations) +* Roslynator analyzers +* Analysis level: latest +* Warnings treated as errors (notably nullable and CS4014) +* **Public APIs require XML documentation**, including protected methods on public types. + +### C# Style Rules (High-level) + +* Allman braces +* 4 spaces, no tabs +* Explicit visibility +* Private/internal fields: `_camelCase`, `readonly` where possible, `static readonly` order +* File-scoped namespaces preferred; using directives outside namespace and sorted +* Use C# keywords (`int`, `string`) rather than BCL types +* Prefer modern C# features where appropriate (nullable, pattern matching, switch expressions, records, init, target-typed new, etc.) +* Use `nameof()` over string literals +* Avoid `this.` unless necessary +* Use `var` when it improves readability + +If a specific file already follows a local style, adhere to existing file conventions. + +--- + +## Zero Pragma Policy (Critical) + +**No `#pragma warning disable`** in production code. + +* **StyleCop warnings (SA****) must be fixed**, never suppressed. +* **No analyzer warning (CA****, S**** Sonar, RCS**** Roslynator, IL**** trimming/AOT, etc.) may be suppressed without explicit human approval** — see "Suppressions: Last Resort Only" above. Fix the code first; if a suppression seems unavoidable, stop and ask. + +Example: + +```csharp +// WRONG +#pragma warning disable CA1062 +public void MyMethod(object parameter) +{ + parameter.ToString(); +} +#pragma warning restore CA1062 + +// CORRECT +public void MyMethod(object parameter) +{ + ArgumentNullException.ThrowIfNull(parameter); + parameter.ToString(); +} + +// LAST RESORT ONLY +[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", + Justification = "TUnit guarantees non-null parameters from data sources.")] +public async Task MyTest(IConverter converter, int expectedValue) +{ + var result = converter.GetValue(); + await Assert.That(result).IsEqualTo(expectedValue); +} +``` + +--- + +## Testing Guidelines + +* Use TUnit + Microsoft Testing Platform +* Write unit tests for new features and bug fixes +* Prefer existing patterns in: + + * `src/tests/ReactiveUI.Tests/` + * `src/tests/ReactiveUI.AOTTests/` +* Use `ReactiveUI.Testing` utilities for reactive code + +--- + +## Common Development Patterns + +### ViewModel Skeleton + +```csharp +public class SampleViewModel : ReactiveObject +{ + private string? _name; + private readonly ObservableAsPropertyHelper _isValid; + + public SampleViewModel() + { + _isValid = this.WhenAnyValue(x => x.Name) + .Select(name => !string.IsNullOrWhiteSpace(name)) + .ToProperty(this, nameof(IsValid)); + + SubmitCommand = ReactiveCommand.CreateFromTask( + ExecuteSubmit, + this.WhenAnyValue(x => x.IsValid)); + } + + public string? Name + { + get => _name; + set => this.RaiseAndSetIfChanged(ref _name, value); + } + + public bool IsValid => _isValid.Value; + + public ReactiveCommand SubmitCommand { get; } + + private async Task ExecuteSubmit(CancellationToken cancellationToken) + { + // Implementation + } +} +``` + +### RxSchedulers (Preferred) + +```csharp +public IObservable GetData() +{ + return Observable.Return("data") + .ObserveOn(RxSchedulers.MainThreadScheduler); +} +``` + +### WhenAnyValue + +```csharp +this.WhenAnyValue( + x => x.FirstName, + x => x.LastName, + (first, last) => $"{first} {last}") + .Subscribe(fullName => { /* handle */ }); + +this.WhenAnyValue(x => x.IsLoading) + .Where(isLoading => !isLoading) + .Subscribe(_ => { /* handle */ }); +``` + +### ObservableAsPropertyHelper + +```csharp +private readonly ObservableAsPropertyHelper _total; +public decimal Total => _total.Value; + +_total = this.WhenAnyValue( + x => x.Quantity, + x => x.Price, + (qty, price) => qty * price) + .ToProperty(this, nameof(Total)); +``` + +--- + +## What to Avoid + +* Reflection-heavy implementations in core paths +* Expression trees in hot paths without caching +* Platform-specific code in `src/ReactiveUI/` core library +* Breaking public APIs without proper versioning and documentation diff --git a/CONTRIBUTING_PERFORMANCE.md b/CONTRIBUTING_PERFORMANCE.md new file mode 100644 index 0000000000..d884ebf2dd --- /dev/null +++ b/CONTRIBUTING_PERFORMANCE.md @@ -0,0 +1,421 @@ +# Contributing — Performance & Engineering Standards + +This document is ReactiveUI's hot-path engineering rule book: the +allocation, async, type-design, and API-shape standards that production +code under `src/ReactiveUI/` (and the platform extension libraries) is +held to. It is the performance-focused companion to +[`agent.md`](agent.md) and the website contribution guide linked from +[`CONTRIBUTING.md`](CONTRIBUTING.md). + +When guidance overlaps: + +- **`agent.md` wins** for build/test commands, repository layout, the + SLNX/MTP/TUnit toolchain, and the AOT guidance (do not duplicate the + AOT section here — see `agent.md` § *AOT Guidance*). +- **This document wins** for hot-path detail: allocation discipline, + the System.Reactive boundary, async rules, type design, and the + perf-driven API-shape rules. +- The website guide (`reactiveui.net/contribute`) remains the narrative + onboarding doc. + +We are tightening performance standards going forward. New code is +expected to meet every rule below. Existing code is migrated +opportunistically — when you touch a file, bring the lines you change up +to standard. + +--- + +## The two tiers: internal vs. public API + +ReactiveUI is a widely consumed framework with a large, long-lived +public surface. The rules below apply at **two different strictness +levels**: + +- **Internal / private code (strict).** Anything not part of the + published API — `private` / `internal` members, sink and operator + implementations, helpers, the expression-rewriting machinery. Apply + every rule in full. **Internal contracts are not sacred:** if a + perf-driven change breaks an internal contract, change the contract. + Internal `[InternalsVisibleTo]` consumers (tests, sibling platform + assemblies) move with it. +- **Public / user-facing API (careful).** Anything a consumer can + compile against — public types, public/protected members, public + return and parameter types, default parameter values already shipped. + New public API follows the rules. Existing public signatures change + only through proper deprecation and versioning (`[Obsolete]` → + removal across a major version), never silently. When a rule below + would break a shipped public signature, treat the rule as a guide for + *new* surface and a *migration target* for old surface — not a license + to break consumers. + +When a rule has a different public-vs-internal answer, it says so +explicitly. + +--- + +## The System.Reactive boundary + +ReactiveUI is built on the Rx contract and that does not change. What is +changing is **who implements the operators**: we are moving toward our +own low-allocation operator and observer *sinks* rather than routing hot +paths through `System.Reactive.Linq.Observable`. + +- **Keep the BCL/Rx contract types — never reinvent them.** + `IObservable`, `IObserver`, `IScheduler` and `Unit` are the + ecosystem interop contract every consumer relies on. Depend on them + directly and pass them through. Authoring parallel substitutes would + fragment the contract for no gain. This is the deliberate exception to + the "build our own" rule. (`IObservable` / `IObserver` live in + the BCL `System` namespace, not `System.Reactive`, so they stay + regardless; `IScheduler` and `Unit` are the only `System.Reactive` + types kept.) +- **Disposables are our own internal implementations — not + `System.Reactive.Disposables`.** We do *not* depend on + `System.Reactive.Disposables`. Use the owned internal primitives, + named to avoid colliding with the `System.Reactive` types a consumer + might also have in scope: + - `DisposableBag` — composite of child disposables + (replaces `CompositeDisposable`). + - `MutableDisposable` — single reassignable slot that does *not* + dispose the previous value on swap + (replaces `MultipleAssignmentDisposable`). + - `SwapDisposable` — single reassignable slot that disposes the + previous value on swap (replaces `SerialDisposable`). + - `OnceDisposable` — write-once slot + (replaces `SingleAssignmentDisposable`). + - `EmptyDisposable` — shared no-op singleton + (replaces `Disposable.Empty`). + - `ActionDisposable` — runs an action on dispose + (replaces `Disposable.Create(Action)`). + These are tailored, low-allocation, and only as thread-aware as the + call site needs — not 1:1 rebadges of the `System.Reactive` shapes. +- **`CompositeDisposable` survives only at the public API edge.** Where + ReactiveUI's *shipped public surface* exposes `CompositeDisposable` + (parameter or return type a consumer compiles against), keep it — that + signature is the contract and changing it breaks consumers. Internally, + and in all new non-public code, use `DisposableBag`. Never introduce + `System.Reactive.Disposables` types into internal code paths. +- **Prefer our own operator/observer sinks on hot paths.** Where a + binding, `WhenAny*`, or command pipeline emits per-value, prefer a + purpose-built sink with the allocation profile we want over chaining + `System.Reactive.Linq` operators (`Select`, `Where`, `CombineLatest`, + `Merge`, `Throttle`, …). A hand-written sink can be `sealed`, avoid + the closure-per-operator overhead, and fuse adjacent stages. +- **Audit new dependencies on `System.Reactive.Linq` before adding + them.** If a hot path "needs" an `Observable.Foo`, the preferred + answer is usually our own sink with the right allocation profile. + Reach into `System.Reactive.Linq` freely in cold paths (one-time + setup, configuration, sample/doc code) and at the public API edge + where we deliberately return composable `IObservable`. +- **`IScheduler` is the scheduling abstraction — pass it through.** + Prefer `RxSchedulers` (AOT-friendly) over `RxApp` per `agent.md`; + never bake a scheduler default into a hot path that should accept one. + +--- + +## Allocation discipline + +The core of the tightened standard. These apply to production code; test +projects relax the allocation rules (see *Tests & benchmarks*). + +- **Zero-LINQ in production code.** No `System.Linq` in production hot + paths. LINQ pulls in lambdas, iterators, and boxed enumerators on + every call. Use plain indexed `for` loops. (LINQ *over `IObservable`* + — the Rx operators — is governed by the System.Reactive boundary + above, not by this rule; this rule is about `System.Linq` over + `IEnumerable`.) +- **`for` over `foreach`.** Indexed `for` over arrays / `Span` / + `ReadOnlySpan` / `List` / `IReadOnlyList`. `foreach` only when + the type genuinely lacks an indexer (`HashSet`, + `IAsyncEnumerable`, dictionary enumeration). +- **`static` lambdas everywhere there is no capture.** + `static x => …` / `static (state, x) => …` lets the JIT skip the + closure allocation. This matters most in the `WhenAnyValue` / binding + selectors that allocate per subscription — pass captured state through + a tuple/state argument rather than closing over locals. +- **Arrays over `List`** when the final length is known up front; + pre-size and write by index. When `List` is unavoidable, **always + pass a `capacity`** (`new List(expectedCount)`) — never + capacity-less. +- **Pre-size `Dictionary` / `HashSet`** with a capacity hint reflecting + the expected size. +- **Avoid `ImmutableArray` / `ImmutableList` on hot paths.** The + wrapping struct adds an indirection per read and the builder churns + intermediate arrays. Reach for an immutable collection only when the + API is genuinely public and consumers must not mutate. Otherwise + expose `IReadOnlyList` / `T[]` and treat it as immutable by + convention. +- **Collection expressions `[..]` first.** `[a, b, ..tail]`, `[]`, + `[..source]` for final materialization. Never `.ToArray()` when a + collection expression does the job. +- **Pool transient buffers.** `ArrayPool.Shared.Rent` paired with a + `try` / `finally` `Return` for transient buffers in pipelines that + allocate per emission. +- **`Interlocked.Increment` / `Interlocked.Decrement`** for simple + counters under contention. Reserve `lock` for genuine multi-field + invariants. +- **`System.Threading.Lock` (net9+) is the default monitor primitive** + for new code that needs a private gate around shared mutable state: + `private readonly Lock _gate = new();` and `lock (_gate)`. On + `net8.0` / `net462` / `net472` / `net481` fall back to + `private readonly object _gate = new();`; hide the multi-TFM split + behind a helper where call-site readability matters. +- **No locks on arbitrary objects** (`this`, `typeof(X)`, public + fields). Always a dedicated `_gate`-style field. + +--- + +## Strings + +ReactiveUI is legitimately string-shaped: property names drive +`RaiseAndSetIfChanged` / `nameof`, binding paths and expression chains +resolve to member names, and the public API exchanges `string` freely. +**We are not adopting a no-`string` / UTF-8-bytes policy.** `string` +stays a first-class type, public and internal. + +What still applies: + +- **Don't allocate strings needlessly on hot paths.** No string + interpolation or `string.Format` inside a per-emission path to build a + value that is then discarded or only used on a failure branch — build + the message lazily, on the throw path. +- **`nameof(...)` over string literals** for member references (already + required by `agent.md`). +- **`StringComparer.Ordinal` / `StringComparison.Ordinal`** for + identifier, type-name, and property-name comparisons and for the + dictionaries/sets keyed on them. Culture-aware comparison is both + wrong here and several times slower. +- **Spans for cheap parsing/slicing.** Prefer `ReadOnlySpan` + + range expressions (`path[..i]`, `name[^1]`) over `Substring` when + picking apart a member path, rather than allocating temporaries. + +--- + +## Async + +Where ReactiveUI is async — `ReactiveCommand.CreateFromTask`, +interaction handlers, async bindings — the pipeline is async end to end. + +- **No sync-over-async.** Never `.GetAwaiter().GetResult()`, `.Result`, + or `.Wait()` inside an async path. If the contract hands you a + `CancellationToken`, you `await`. +- **`ConfigureAwait(false)` on every library `await`** in production + code. Tests don't need it. +- **Cancellation flows through.** Async operators accept a + `CancellationToken` where the contract supports it and pass it down — + never swallow it, never default to `CancellationToken.None` when a real + token is in scope. Create a + `CancellationTokenSource.CreateLinkedTokenSource` once at subscribe + time, not per emission. +- **`ValueTask` first when zero-alloc is proven; `Task` otherwise.** Use + `ValueTask` when most implementations complete synchronously and the + call site multiplies (per-emission paths). Use `Task` when the path is + genuinely async-dominant (I/O) or cold (one call per setup). Obey the + consume-once rule for `ValueTask`: never `await` the same instance + twice, never store it in a field. +- **Sync impls return cached completed tasks** — `=> ValueTask.CompletedTask` + / `Task.CompletedTask`; no state machine, no allocation. + +--- + +## Pattern matching & flow control + +- **Invert `if`s to flatten the happy path.** Guard clauses + early + `return` / `continue` first; main logic stays unindented. No `else` on + a guarded branch. +- **Switch expressions over `if` / `else` chains** — property patterns + (`{ HasValue: true }`), positional patterns, list patterns. Order of + preference: switch expression → switch statement → `if` / `else if` + chain. Reach for the next form only when the prior cannot express the + dispatch (mutating `ref` / `out`, side-effects, fall-through). +- **List patterns for emptiness / cardinality.** `is [_, ..]` over + `.Count > 0`; `is []` for empty; `is [var single]` to bind a + single-element collection. +- **`is` / `is not` over `==` / `!=`** for null and type checks; combine + type test + property check in one line where it reads well. +- **Avoid `while (true)`.** Express the termination condition in the loop + header. The only exception is a genuinely unbounded pump exiting via a + token, which should be `while (!cancellationToken.IsCancellationRequested)`. + +--- + +## API shape + +- **No default parameter values on new APIs.** Default values bake the + constant into every caller's IL — bumping it later needs a recompile of + every consumer. Provide explicit overloads instead, each delegating to + the most-specific overload that takes everything explicitly. + *(Public two-tier note: ReactiveUI's shipped public API already uses + defaults in places — `WhenAnyValue`, `ToProperty`, scheduler args. + Leave those as-is; do not break consumers. Apply this rule to new + public surface and to all internal surface.)* +- **Concrete collection types in new production APIs** where practical — + `IReadOnlyList` / `T[]` / `Dictionary` / `HashSet` over + `IEnumerable` for new parameters and return values. `IEnumerable` + is fine only when a streaming yield genuinely avoids materializing the + sequence. *(The Rx observer contract is already streaming one value at + a time; this rule is about `IEnumerable`-style collection params, not + `IObservable`.)* Existing public signatures that return interface + types stay. +- **Pin the latest non-beta version** when adding to + `src/Directory.Packages.props`. Check + `https://api.nuget.org/v3-flatcontainer//index.json` + for the highest stable release; never `-preview` / `-rc` / `-alpha` / + `-beta`. Same rule for bumps. + +--- + +## Type design + +- **`sealed` every class** that isn't designed for inheritance — the + default for new internal types. Helps inlining and removes accidental + override surface. *(Public base types intended for derivation — + `ReactiveObject`, view base classes — stay open by design.)* +- **`readonly record struct`** for immutable value-shaped data: small + (≤ 4–5 fields) or holding only references. Free equality/hashing, no GC + pressure. +- **`sealed record` (class)** when the record participates in an + inheritance hierarchy or holds many fields. +- **Most methods static.** A method that doesn't touch `this` should be + `static` — fewer hidden allocations, clearer call sites, free + devirtualization. If a class ends up with only static methods, mark the + class `static` too. +- **`internal static` helpers** for stateless cross-type utilities. + Group by responsibility; keep the public surface narrow. +- **Singleton comparers** (`private sealed class XComparer : IComparer` + with `public static readonly XComparer Instance`) instead of + allocating a fresh comparer/lambda per `Array.Sort` / dictionary. +- **Bundle long parameter lists into a `readonly record struct` or + `ref struct`** rather than splitting the method. The state type + documents the relationship between values and lets the JIT keep them in + registers. + +--- + +## Properties + +- **C# `field` keyword by default.** When a property needs backing logic + (lazy init, validation, change-tracking), use `field` inside the + accessors rather than a separate `_name` field. Keep an explicit + backing field only for: + - **`ref`-passing APIs** — `Interlocked.Increment(ref _counter)`, + `Volatile.Read(ref _state)`, `Unsafe.As(ref _slot)`. Document + with a one-line comment. + - **Constructor assignment that must bypass setter logic.** + - **Storage referenced from a method outside the property** (rare). +- **`RaiseAndSetIfChanged` stays the canonical reactive setter.** The + `field`-keyword guidance is for non-reactive backing logic; reactive + properties continue to use `this.RaiseAndSetIfChanged(ref field, value)` + with an explicit backing field (it is a `ref`-passing API — the first + exception above). + +--- + +## Exception helpers, spans, and read-mostly lookups + +- **Exception helpers compose their own messages.** Prefer + `ArgumentNullException.ThrowIfNull(x)` and + `[CallerArgumentExpression]`-based helpers over hand-written + `if (x is null) throw …`; call sites pass only the value, never + `nameof(x)`. (Matches the `agent.md` zero-pragma `ThrowIfNull` example.) +- **`SearchValues` for repeated multi-character searches.** Cache as + `private static readonly SearchValues` and pass to + `IndexOfAny` / `IndexOfAnyExcept` — faster than `IndexOfAny([...])` + anywhere hit more than once. +- **`TryFormat` / `TryParse` over `ToString` / `Parse`** when writing + into a span buffer. +- **`FrozenDictionary` / `FrozenSet` only when all four hold:** + built once → queried many times → read-only after construction → + genuinely hot or broadly shared. The freeze pass is expensive; do not + use `Frozen*` for per-instance, short-lived, or per-subscription + tables — a plain `Dictionary` / `HashSet` with the right comparer wins + there. + +--- + +## Analyzers & suppressions + +ReactiveUI runs StyleCop, Roslynator, the .NET CA analyzers, and now +`SonarAnalyzer.CSharp` and `Blazor.Common.Analyzers`. These catch real +perf and correctness issues. + +- **Fix the code, don't silence the rule.** Almost every analyzer hit has + a structural fix — pull out a helper, invert a guard, change a return + type, restructure a throw. That is preferable to suppression. +- **Zero-pragma policy (see `agent.md`).** No `#pragma warning disable` + in production code. StyleCop (`SA****`) warnings must be *fixed*, never + suppressed. +- **`[SuppressMessage]` is a last resort and requires justification.** + Use a per-symbol `[SuppressMessage("Category", "RuleId", Justification = "…")]` + naming a concrete reason. CA-rule suppression is allowed only when a + fix would genuinely harm the design; a second hit on the same rule + usually means the design is wrong — fix that instead. +- **Zero `` policy in production projects.** Project-wide + `` in `.csproj` / `.props` / `.targets` needs explicit + consultation and is unlikely to be approved. + +--- + +## Commit style + +We follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/) +so `git log` is mechanically scannable and release-notes tooling can +group by intent. + +``` +(): + + + + +``` + +**Types:** `feat`, `fix`, `perf`, `refactor`, `docs`, `test`, `build`, +`ci`, `chore`, `revert` — standard meanings. + +**Scope** is the affected subsystem, lowercase, no `ReactiveUI.` prefix. +Examples: `binding`, `whenany`, `command`, `activation`, `routing`, +`interactions`, `builder`, `scheduler`, plus platform scopes (`wpf`, +`winui`, `maui`, `androidx`, `blazor`, `winforms`). Omit the scope when a +change spans many areas evenly. + +**Subject:** ~70 chars, imperative mood, lowercase initial, no trailing +period. + +**Body:** explains the *why*. **For `perf` commits, include benchmark +numbers** (before/after, scenario, allocation delta) so the win is +verifiable. + +**Footers:** `BREAKING CHANGE: ` (or `!` after the type, e.g. +`feat(binding)!:`) for any public-API change; reference issues +(`Closes #123`, `Refs #456`). + +Example: + +``` +perf(whenany): cut per-subscription alloc by hoisting the selector to a static lambda + +Replace the captured-closure selector in the two-property WhenAnyValue +overload with a static lambda that threads the source through a value +tuple. The closure object is gone from the subscription path. +Verified on WhenAnyValueBenchmarks: 92 ns -> 61 ns, alloc 64 B -> 0 B. + +Refs #1234 +``` + +--- + +## Tests & benchmarks + +- **TUnit + Microsoft Testing Platform** under `src/tests/` (see + `agent.md` for commands). Treat tests as documentation — names and + asserts communicate the contract. Prefer real implementations over + mocks in integration tests. +- **Test allocation rules are relaxed.** `foreach`, `System.Linq`, and + capacity-less `List` are fine in tests where readability beats + micro-optimization. The style, pattern-matching, and suppression rules + still apply. +- **BenchmarkDotNet** under `Benchmarks/` for hot-path work. Always + include `[MemoryDiagnoser]` and track allocations alongside throughput. + Add a benchmark when you add or change a hot-path feature; cite its + numbers in the `perf` commit body. diff --git a/MERGE_FOLLOWUPS.md b/MERGE_FOLLOWUPS.md new file mode 100644 index 0000000000..f526646c2b --- /dev/null +++ b/MERGE_FOLLOWUPS.md @@ -0,0 +1,46 @@ +# Rebase follow-ups + +This branch was rebased onto `origin/main` while it carries a large System.Reactive/DynamicData +removal and a fused-sink rewrite. Several `main` PRs changed code in regions this branch had already +rewritten. Where the upstream fix could be cleanly ported onto the rewrite it was done during the +rebase; where the original Rx-based code was wholly replaced, the rewrite was kept and the fix is +listed here to be re-derived and re-validated against the new architecture. + +## Ported during the rebase (no further action expected) + +- **#4351** interaction async handler scheduling — `Interactions/Interaction.cs`: `YieldToCurrentContext()` + + `RegisterHandlerCore` re-applied over the `TaskUnitObservable`/`ToUnitObservable` sinks. +- **#4353** suspension persistence (materialize app state before shutdown save) — `Suspension/SuspensionHostExtensions.cs`: + the persist path now runs the pending one-time load (`RunPendingLoad()`) before `SaveState`. +- **#4349** null load state — `Suspension/SuspensionHostExtensions.cs`: load falls back to + `CreateNewAppState`/`CreateNewAppStateTyped` when the driver yields `null`. +- **#4358** WPF/winforms design-time activation — `Activation/ViewForMixins.cs`: design-mode early-return + (no throw) re-applied. The WPF-specific `WpfPropertyBinderImplementation` registration was re-added in + `ReactiveUI.Wpf/Registrations.cs`. + +## Kept the rewrite — upstream fix needs re-validation / re-porting + +These files were fully reimplemented on this branch (fused sinks / request objects), so the upstream +fix was entangled with code that no longer exists. The rewrite was taken; re-derive the fix's intent +against the new code and restore the upstream test coverage. + +- **#4324** `BindCommand` passes the wrong parameter after the ViewModel is reassigned — + `Bindings/Command/CommandBinderImplementation.cs` (and `Bindings/Command/CommandBinder.cs`). Check the + `string.IsNullOrEmpty(toEvent)` rebinding-customizer gate and parameter re-evaluation on rebind. +- **#4350** inherited `DependencyProperty` lookup — `Bindings/Property/PropertyBinderImplementation.cs`, + `Bindings/Property/PropertyBindingMixins.cs`, `ReactiveUI.Wpf/WpfReactiveUIBuilderExtensions.cs`. + (`WpfPropertyBinderImplementation` is registered again; confirm the rewritten property binder honours + inherited DP metadata.) +- **WPF command rebinding dispatcher marshalling** — `ReactiveUI.Wpf/WpfCommandRebindingCustomizer.cs`: + upstream marshalled `SetValue` to the UI thread via `Dispatcher.BeginInvoke` when off-thread; confirm + the rewritten customizer preserves that. +- **#4337 mobile cache tuning** — `Builder/ReactiveUIBuilder.cs`: upstream used smaller small/big cache + limits on `ANDROID`/`IOS`; the rewrite uses fixed `DefaultSmallCacheLimit`/`DefaultBigCacheLimit` + constants. Decide whether the per-platform tuning should return. + +## Tests taken as ours + +`tests/ReactiveUI.Tests/InteractionsTest.cs`, `SuspensionHostExtensionsTests.cs`, +`ReactiveUI.Wpf.Tests/Wpf/ValidationBindingWpfTest.cs`, +`ReactiveUI.Blazor.Tests/BlazorReactiveUIBuilderExtensionsTests.cs` kept the branch's versions. Re-add +the upstream test cases that cover the fixes listed above once those are re-ported. diff --git a/README.md b/README.md index 4edc8ae1c5..bee07e8a0b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Build](https://github.com/reactiveui/ReactiveUI/actions/workflows/ci-build.yml/badge.svg)](https://github.com/reactiveui/ReactiveUI/actions/workflows/ci-build.yml) [![Code Coverage](https://codecov.io/gh/reactiveui/ReactiveUI/branch/main/graph/badge.svg)](https://codecov.io/gh/reactiveui/ReactiveUI) -[![#yourfirstpr](https://img.shields.io/badge/first--timers--only-friendly-blue.svg)](https://reactiveui.net/contribute) +[![#yourfirstpr](https://img.shields.io/badge/first--timers--only-friendly-blue.svg)](https://reactiveui.net/contribute) [![](https://img.shields.io/badge/chat-slack-blue.svg)](https://reactiveui.net/slack)
@@ -11,7 +11,10 @@ # What is ReactiveUI? -[ReactiveUI](https://reactiveui.net/) is a composable, cross-platform model-view-viewmodel framework for all .NET platforms that is inspired by functional reactive programming, which is a paradigm that allows you to [abstract mutable state away from your user interfaces and express the idea around a feature in one readable place](https://www.youtube.com/watch?v=3HwEytvngXk) and improve the testability of your application. +[ReactiveUI](https://reactiveui.net/) is a composable, cross-platform model-view-viewmodel framework for all .NET +platforms that is inspired by functional reactive programming, which is a paradigm that allows you +to [abstract mutable state away from your user interfaces and express the idea around a feature in one readable place](https://www.youtube.com/watch?v=3HwEytvngXk) +and improve the testability of your application. [🔨 Get Started](https://reactiveui.net/docs/getting-started/) [🛍 Install Packages](https://reactiveui.net/docs/getting-started/installation/) [🎞 Watch Videos](https://reactiveui.net/docs/resources/videos) [🎓 View Samples](https://reactiveui.net/docs/resources/samples/) [🎤 Discuss ReactiveUI](https://reactiveui.net/slack) @@ -20,105 +23,149 @@ - [RxSchedulers](docs/RxSchedulers.md) - Using ReactiveUI schedulers without RequiresUnreferencedCode attributes ## Book -There has been an excellent [book](https://kent-boogaart.com/you-i-and-reactiveui/) written by our Alumni maintainer Kent Boogart. + +There has been an excellent [book](https://kent-boogaart.com/you-i-and-reactiveui/) written by our Alumni maintainer +Kent Boogart. ## NuGet Packages -Install the following packages to start building your own ReactiveUI app. Note: some of the platform-specific packages are required. This means your app won't perform as expected until you install the packages properly. See the Installation docs page for more info. - -| Platform | ReactiveUI Package | NuGet | -| ----------------- | ----------------------------------- | ---------------------- | -| .NET Standard | [ReactiveUI][CoreDoc] | [![CoreBadge]][Core] | -| Any | [ReactiveUI.SourceGenerators][SGDoc]| [![SGBadge]][SG] | -| Unit Testing | [ReactiveUI.Testing][TestDoc] | [![TestBadge]][Test] | -| WPF | [ReactiveUI.WPF][WpfDoc] | [![WpfBadge]][Wpf] | -| WinUI | [ReactiveUI.WinUI][WinUiDoc] | [![WinUiBadge]][WinUi] | -| MAUI | [ReactiveUI.Maui][MauiDoc] | [![MauiBadge]][Maui] | -| Windows Forms | [ReactiveUI.WinForms][WinDoc] | [![WinBadge]][Win] | -| AndroidX | [ReactiveUI.AndroidX][DroDoc] | [![DroXBadge]][DroX] | -| Blazor | [ReactiveUI.Blazor][BlazDoc] | [![BlazBadge]][Blaz] | -| Platform Uno | [ReactiveUI.Uno][UnoDoc] | [![UnoBadge]][Uno] | -| Platform Uno | [ReactiveUI.Uno.WinUI][UnoWinUiDoc] | [![UnoWinUiBadge]][UnoWinUi] | -| Avalonia | [ReactiveUI.Avalonia][AvaDoc] | [![AvaBadge]][Ava] | -| Any | [ReactiveUI.Validation][ValDocs] | [![ValBadge]][ValCore] | -| Any | [ReactiveUI.Extensions][ExtDocs] | [![ExtBadge]][Ext] | +Install the following packages to start building your own ReactiveUI app. Note: some of the platform-specific +packages are required. This means your app won't perform as expected until you install the packages properly. See +the Installation docs page for more info. + +| Platform | ReactiveUI Package | NuGet | +|---------------|--------------------------------------|------------------------------| +| .NET Standard | [ReactiveUI][CoreDoc] | [![CoreBadge]][Core] | +| Any | [ReactiveUI.SourceGenerators][SGDoc] | [![SGBadge]][SG] | +| Unit Testing | [ReactiveUI.Testing][TestDoc] | [![TestBadge]][Test] | +| WPF | [ReactiveUI.WPF][WpfDoc] | [![WpfBadge]][Wpf] | +| WinUI | [ReactiveUI.WinUI][WinUiDoc] | [![WinUiBadge]][WinUi] | +| MAUI | [ReactiveUI.Maui][MauiDoc] | [![MauiBadge]][Maui] | +| Windows Forms | [ReactiveUI.WinForms][WinDoc] | [![WinBadge]][Win] | +| AndroidX | [ReactiveUI.AndroidX][DroDoc] | [![DroXBadge]][DroX] | +| Blazor | [ReactiveUI.Blazor][BlazDoc] | [![BlazBadge]][Blaz] | +| Platform Uno | [ReactiveUI.Uno][UnoDoc] | [![UnoBadge]][Uno] | +| Platform Uno | [ReactiveUI.Uno.WinUI][UnoWinUiDoc] | [![UnoWinUiBadge]][UnoWinUi] | +| Avalonia | [ReactiveUI.Avalonia][AvaDoc] | [![AvaBadge]][Ava] | +| Any | [ReactiveUI.Validation][ValDocs] | [![ValBadge]][ValCore] | +| Any | [ReactiveUI.Extensions][ExtDocs] | [![ExtBadge]][Ext] | [Core]: https://www.nuget.org/packages/ReactiveUI/ + [CoreBadge]: https://img.shields.io/nuget/v/ReactiveUI.svg + [CoreDoc]: https://reactiveui.net/docs/getting-started/installation/ [SG]: https://www.nuget.org/packages/ReactiveUI.SourceGenerators/ + [SGDoc]: https://reactiveui.net/docs/handbook/view-models/boilerplate-code + [SGBadge]: https://img.shields.io/nuget/v/ReactiveUI.SourceGenerators.svg [Test]: https://www.nuget.org/packages/ReactiveUI.Testing/ + [TestBadge]: https://img.shields.io/nuget/v/ReactiveUI.Testing.svg + [TestDoc]: https://reactiveui.net/docs/handbook/testing/ [Wpf]: https://www.nuget.org/packages/ReactiveUI.WPF/ + [WpfBadge]: https://img.shields.io/nuget/v/ReactiveUI.WPF.svg + [WpfDoc]: https://reactiveui.net/docs/getting-started/installation/windows-presentation-foundation [WinUi]: https://www.nuget.org/packages/ReactiveUI.WinUI/ + [WinUiBadge]: https://img.shields.io/nuget/v/ReactiveUI.WinUI.svg + [WinUiDoc]: https://reactiveui.net/docs/getting-started/installation/universal-windows-platform [Maui]: https://www.nuget.org/packages/ReactiveUI.Maui/ + [MauiBadge]: https://img.shields.io/nuget/v/ReactiveUI.Maui.svg + [MauiDoc]: https://blog.jetbrains.com/dotnet/2020/09/18/xamarin-maui-and-the-reactive-mvvm-between-them-webinar-recording/ [Win]: https://www.nuget.org/packages/ReactiveUI.WinForms/ + [WinEvents]: https://www.nuget.org/packages/ReactiveUI.Events.WinForms/ + [WinBadge]: https://img.shields.io/nuget/v/ReactiveUI.WinForms.svg + [WinDoc]: https://reactiveui.net/docs/getting-started/installation/windows-forms [DroX]: https://www.nuget.org/packages/ReactiveUI.AndroidX/ + [DroXBadge]: https://img.shields.io/nuget/v/ReactiveUI.AndroidX.svg + [DroDoc]: https://reactiveui.net/docs/getting-started/installation/ [Uno]: https://www.nuget.org/packages/ReactiveUI.Uno/ + [UnoBadge]: https://img.shields.io/nuget/v/ReactiveUI.Uno.svg + [UnoDoc]: https://reactiveui.net/docs/getting-started/installation/uno-platform + [UnoWinUi]: https://www.nuget.org/packages/ReactiveUI.Uno.WinUI/ + [UnoWinUiBadge]: https://img.shields.io/nuget/v/ReactiveUI.Uno.WinUI.svg + [UnoWinUiDoc]: https://reactiveui.net/docs/getting-started/installation/uno-platform [Blaz]: https://www.nuget.org/packages/ReactiveUI.Blazor/ + [BlazBadge]: https://img.shields.io/nuget/v/ReactiveUI.Blazor.svg + [BlazDoc]: https://www.reactiveui.net/docs/getting-started/installation/blazor [Ava]: https://www.nuget.org/packages/ReactiveUI.Avalonia/ + [AvaBadge]: https://img.shields.io/nuget/v/ReactiveUI.Avalonia.svg + [AvaDoc]: https://reactiveui.net/docs/getting-started/installation/avalonia + [EventsDocs]: https://reactiveui.net/docs/handbook/events/ [ValCore]: https://www.nuget.org/packages/ReactiveUI.Validation/ + [ValBadge]: https://img.shields.io/nuget/v/ReactiveUI.Validation.svg + [ValDocs]: https://reactiveui.net/docs/handbook/user-input-validation/ [Ext]: https://www.nuget.org/packages/ReactiveUI.Extensions/ + [ExtBadge]: https://img.shields.io/nuget/v/ReactiveUI.Extensions.svg + [ExtDocs]: https://reactiveui.net/ ## Sponsorship -The core team members, ReactiveUI contributors and contributors in the ecosystem do this open-source work in their free time. If you use ReactiveUI, a serious task, and you'd like us to invest more time on it, please donate. This project increases your income/productivity too. It makes development and applications faster and it reduces the required bandwidth. +The core team members, ReactiveUI contributors and contributors in the ecosystem do this open-source work in their free +time. If you use ReactiveUI, a serious task, and you'd like us to invest more time on it, please donate. This project +increases your income/productivity too. It makes development and applications faster and it reduces the required +bandwidth. [Become a sponsor](https://github.com/sponsors/reactivemarbles). ## Migration from Xamarin and .NET 8 MAUI ### Xamarin Users -As of May 2024, Microsoft ended support for Xamarin per their [support policy](https://docs.microsoft.com/dotnet/maui/what-is-maui#xamarin-retirement). ReactiveUI has removed support for legacy Xamarin platforms in favor of modern .NET MAUI. For Xamarin projects: + +As of May 2024, Microsoft ended support for Xamarin per +their [support policy](https://docs.microsoft.com/dotnet/maui/what-is-maui#xamarin-retirement). ReactiveUI has removed +support for legacy Xamarin platforms in favor of modern .NET MAUI. For Xamarin projects: - **Xamarin.Forms** → Migrate to **MAUI** and use `ReactiveUI.Maui` - **Xamarin.Android** → Migrate to **MAUI Android** or use `ReactiveUI.AndroidX` for native Android - **Xamarin.iOS/Mac** → Migrate to **MAUI iOS/Mac Catalyst** -For guidance on migrating from Xamarin to MAUI, see the [official migration documentation](https://docs.microsoft.com/dotnet/maui/migration/). +For guidance on migrating from Xamarin to MAUI, see +the [official migration documentation](https://docs.microsoft.com/dotnet/maui/migration/). ### MAUI Users + ReactiveUI supports .NET 9 and .NET 10 for MAUI platforms: + - `net10.0-android` / `net9.0-android` - `net10.0-ios` / `net9.0-ios` - `net10.0-maccatalyst` / `net9.0-maccatalyst` @@ -130,15 +177,16 @@ Non-MAUI `net8.0` library targets remain fully supported. Platform-specific sample applications are included in [`src/examples/`](src/examples/): -| Sample | Platform | Description | -|--------|----------|-------------| -| [ReactiveUI.Samples.Wpf](src/examples/ReactiveUI.Samples.Wpf) | WPF | Login form with reactive bindings, PasswordBox event marshaling | -| [ReactiveUI.Samples.Winforms](src/examples/ReactiveUI.Samples.Winforms) | WinForms | Login form with IViewFor, programmatic UI layout | -| [ReactiveUI.Samples.Maui](src/examples/ReactiveUI.Samples.Maui) | MAUI | Cross-platform login with Shell navigation, ReactiveContentPage | -| [ReactiveUI.Builder.WpfApp](src/examples/ReactiveUI.Builder.WpfApp) | WPF | Multi-instance chat app with routing, suspension, and network sync | -| [ReactiveUI.Builder.BlazorServer](src/examples/ReactiveUI.Builder.BlazorServer) | Blazor Server | Chat app with server-side Blazor and reactive components | +| Sample | Platform | Description | +|---------------------------------------------------------------------------------|---------------|--------------------------------------------------------------------| +| [ReactiveUI.Samples.Wpf](src/examples/ReactiveUI.Samples.Wpf) | WPF | Login form with reactive bindings, PasswordBox event marshaling | +| [ReactiveUI.Samples.Winforms](src/examples/ReactiveUI.Samples.Winforms) | WinForms | Login form with IViewFor, programmatic UI layout | +| [ReactiveUI.Samples.Maui](src/examples/ReactiveUI.Samples.Maui) | MAUI | Cross-platform login with Shell navigation, ReactiveContentPage | +| [ReactiveUI.Builder.WpfApp](src/examples/ReactiveUI.Builder.WpfApp) | WPF | Multi-instance chat app with routing, suspension, and network sync | +| [ReactiveUI.Builder.BlazorServer](src/examples/ReactiveUI.Builder.BlazorServer) | Blazor Server | Chat app with server-side Blazor and reactive components | -All samples target .NET 10, use `RxAppBuilder` for initialization, and demonstrate `WhenActivated`, `Bind`/`BindCommand`, and proper subscription disposal. +All samples target .NET 10, use `RxAppBuilder` for initialization, and demonstrate `WhenActivated`, `Bind`/ +`BindCommand`, and proper subscription disposal. This is how we use the donations: @@ -148,17 +196,23 @@ This is how we use the donations: ## Support -If you have a question, please see if any discussions in our [GitHub issues](https://github.com/reactiveui/ReactiveUI/issues) or [Stack Overflow](https://stackoverflow.com/questions/tagged/reactiveui) have already answered it. +If you have a question, please see if any discussions in +our [GitHub issues](https://github.com/reactiveui/ReactiveUI/issues) +or [Stack Overflow](https://stackoverflow.com/questions/tagged/reactiveui) have already answered it. -If you want to discuss something or just need help, here is our [Slack room](https://reactiveui.net/slack), where there are always individuals looking to help out! +If you want to discuss something or just need help, here is our [Slack room](https://reactiveui.net/slack), where there +are always individuals looking to help out! Please do not open GitHub issues for support requests. ## Contribute -ReactiveUI is developed under an OSI-approved open source license, making it freely usable and distributable, even for commercial use. +ReactiveUI is developed under an OSI-approved open source license, making it freely usable and distributable, even for +commercial use. -If you want to submit pull requests please first open a [GitHub issue](https://github.com/reactiveui/ReactiveUI/issues/new/choose) to discuss. We are first time PR contributors friendly. +If you want to submit pull requests please first open +a [GitHub issue](https://github.com/reactiveui/ReactiveUI/issues/new/choose) to discuss. We are first time PR +contributors friendly. See [Contribution Guidelines](https://www.reactiveui.net/contribute/) for further information how to contribute changes. @@ -197,7 +251,6 @@ See [Contribution Guidelines](https://www.reactiveui.net/contribute/) for furthe - ## Alumni Core Team The following have been core team members in the past. @@ -255,7 +308,8 @@ The following have been core team members in the past. - ## .NET Foundation -ReactiveUI is part of the [.NET Foundation](https://www.dotnetfoundation.org/). Other projects that are associated with the foundation include the Microsoft .NET Compiler Platform ("Roslyn") as well as the Microsoft ASP.NET family of projects, and Microsoft .NET Core. +ReactiveUI is part of the [.NET Foundation](https://www.dotnetfoundation.org/). Other projects that are associated with +the foundation include the Microsoft .NET Compiler Platform ("Roslyn") as well as the Microsoft ASP.NET family of +projects, and Microsoft .NET Core. diff --git a/agent.md b/agent.md index 6f7338f0da..7e8ef1922b 100644 --- a/agent.md +++ b/agent.md @@ -1,378 +1,5 @@ # agent.md -This file is the single source of truth for AI/agent assistance in this repository (Claude Code, GitHub Copilot, and other coding agents). It consolidates build/test commands, architecture context, coding standards, and AOT guidance. +The canonical agent guidance for this repository lives in **[CLAUDE.md](./CLAUDE.md)**. -If there is any conflict between other agent instruction files and this file, follow **agent.md**. - ---- - -## Repository Orientation - -- **Repository root** -- **Primary working directory for build/test:** `./src` -- **Main solution:** `src/reactiveui.slnx` -- **Benchmarks solution:** `Benchmarks/ReactiveUI.Benchmarks.sln` -- **Integration tests:** `integrationtests/` (platform-specific solutions; not required for most tasks) - -### Full Clone Required - -**CRITICAL:** Use a full, recursive clone. Shallow clones can fail because build/versioning relies on git history. If a clone has already been done you must use the unshallow commit command in git. - -```bash -git clone --recursive https://github.com/reactiveui/reactiveui.git -```` - ---- - -## Solution Format: SLNX - -This repository uses **SLNX** (XML-based solution format) instead of legacy `.sln`. - -* Introduced in Visual Studio 2022 17.10+ -* Rider 2024.1+ support -* Works with `dotnet build/test` the same way `.sln` does -* Main file: `src/reactiveui.slnx` - ---- - -## Build Environment Requirements - -### Required SDKs - -* .NET **8.0**, **9.0**, **10.0** SDKs (all required) - -### Workload Restore (Required) - -**CRITICAL:** Platform workloads must be restored or the build will fail. Run from the `./src` directory. - -```powershell -dotnet --info - -cd src -dotnet workload restore -cd .. -``` - -### Restore & Build - -**CRITICAL:** Run build/test commands from `./src` unless the command explicitly uses `src/`-prefixed paths. - -```powershell -cd src - -dotnet restore reactiveui.slnx - -dotnet build reactiveui.slnx -c Release -dotnet build reactiveui.slnx -c Release -warnaserror - -dotnet clean reactiveui.slnx -``` - -### Windows Requirements - -Building the full solution requires **Windows** due to Windows-only target frameworks (WPF, WinUI, .NET Framework). Non-Windows builds may fail; this is expected. In non-Windows environments, focus on documentation, targeted library changes, or analysis that does not require full compilation. - ---- - -## Testing: Microsoft Testing Platform (MTP) + TUnit - -This repo uses **Microsoft Testing Platform (MTP)** with **TUnit**. This differs from VSTest. - -* MTP is configured via `global.json` -* Additional test settings in `testconfig.json` -* Test projects enable `TestingPlatformDotnetTestSupport` in `Directory.Build.props` - -**Key rule:** TUnit/MTP arguments go **after** `--`. - -### Testing Best Practices - -* **Do NOT use `--no-build`**. Always build before testing to avoid stale binaries. -* To see test output, use `--output Detailed` **before** `--`. -* Repository configuration runs tests **non-parallel** (`"parallel": false` in `testconfig.json`) to avoid interference. - -### Test Commands (run from `./src`) - -```powershell -cd src - -# Run all tests -dotnet test --solution reactiveui.slnx -c Release - -# Run tests for a specific project -dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj - -# Run with code coverage (Microsoft Code Coverage) -dotnet test --solution reactiveui.slnx --coverage --coverage-output-format cobertura - -# Detailed output (place BEFORE --) -dotnet test --solution reactiveui.slnx -- --output Detailed -dotnet test --solution reactiveui.slnx --coverage --coverage-output-format cobertura -- --report-trx --output Detailed - -# List tests -dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --list-tests - -# Fail fast -dotnet test --solution reactiveui.slnx -- --fail-fast - -# Limit parallelism if needed (even though repo defaults non-parallel) -dotnet test --solution reactiveui.slnx -- --maximum-parallel-tests 4 -``` - -### TUnit `--treenode-filter` Syntax - -Pattern: `/{AssemblyName}/{Namespace}/{ClassName}/{TestMethodName}` - -Examples: - -```powershell -# Single test -dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --treenode-filter "/*/*/*/MyTestMethod" - -# All tests in class -dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --treenode-filter "/*/*/MyClassName/*" - -# All tests in namespace -dotnet test --project tests/ReactiveUI.Tests/ReactiveUI.Tests.csproj -- --treenode-filter "/*/MyNamespace/*/*" - -# Filter by property (e.g., Category) -dotnet test --solution reactiveui.slnx -- --treenode-filter "/*/*/*/*[Category=Integration]" -``` - -See: [https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test?tabs=dotnet-test-with-mtp](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test?tabs=dotnet-test-with-mtp) -TUnit flags reference: [https://tunit.dev/docs/reference/command-line-flags](https://tunit.dev/docs/reference/command-line-flags) - ---- - -## Key Configuration Files - -* `src/global.json` — sets `"Microsoft.Testing.Platform"` runner -* `src/testconfig.json` — test execution settings (parallel false, coverage format, etc.) -* `src/Directory.Build.props` — repository-wide build configuration (incl. `TestingPlatformDotnetTestSupport`) -* `.github/copilot-instructions.md` — may exist, but should defer to this `agent.md` - ---- - -## Architecture Overview - -ReactiveUI is a cross-platform MVVM framework built on Rx.NET and functional reactive programming principles. - -### Core Library (`src/ReactiveUI/`) - -* `ReactiveObject/` — reactive `INotifyPropertyChanged` base -* `ReactiveCommand/` — observable command pipelines -* `Activation/` — view/viewmodel activation lifecycle -* `Bindings/` — one-way/two-way binding infrastructure -* `Expression/` — expression tree analysis for observation (`WhenAnyValue`) -* `Routing/` — navigation/routing -* `Interactions/` — request/response patterns -* `Builder/` — DI and service registration patterns - -### Platform Extensions - -Examples: - -* `ReactiveUI.Wpf/`, `ReactiveUI.WinUI/`, `ReactiveUI.Maui/`, `ReactiveUI.AndroidX/`, - `ReactiveUI.Blazor/`, `ReactiveUI.Winforms/`, `ReactiveUI.Testing/`, etc. - -### Scheduler Abstraction - -* Prefer `RxSchedulers` (AOT-friendly, avoids reflection/AOT attribute propagation) -* Use `RxApp` only when required (e.g., unit test scheduler detection) - -See `docs/RxSchedulers.md`. - ---- - -## AOT Guidance (Critical) - -This repository targets net8.0+ and supports AOT/trimming scenarios. - -### Primary Rule: Avoid Reflection Paths - -Prefer strongly-typed and source-generator-friendly approaches. Avoid reflection-heavy patterns that require trimming/AOT attributes. - -### Attributes: Use Only If Necessary - -* Avoid introducing DAC/RDC/RUC attributes unless required. -* If an attribute is required, apply it directly (no `#if NET6_0_OR_GREATER` guards). Polyfills are available. - -Example (only when truly needed): - -```csharp -private static object CreateInstance( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - Type type) -{ - return Activator.CreateInstance(type)!; -} -``` - -### Suppressions: Last Resort Only - -If a warning cannot be resolved without harming design, use suppression attributes with a clear justification. Prefer minimal scope and specific suppression IDs. - ---- - -## Code Style & Quality Requirements - -**CRITICAL:** Follow ReactiveUI contribution guidelines: -[https://www.reactiveui.net/contribute/index.html](https://www.reactiveui.net/contribute/index.html) - -### Enforced Tooling - -* `.editorconfig` formatting/naming conventions -* StyleCop analyzers (build fails on violations) -* Roslynator analyzers -* Analysis level: latest -* Warnings treated as errors (notably nullable and CS4014) -* **Public APIs require XML documentation**, including protected methods on public types. - -### C# Style Rules (High-level) - -* Allman braces -* 4 spaces, no tabs -* Explicit visibility -* Private/internal fields: `_camelCase`, `readonly` where possible, `static readonly` order -* File-scoped namespaces preferred; using directives outside namespace and sorted -* Use C# keywords (`int`, `string`) rather than BCL types -* Prefer modern C# features where appropriate (nullable, pattern matching, switch expressions, records, init, target-typed new, etc.) -* Use `nameof()` over string literals -* Avoid `this.` unless necessary -* Use `var` when it improves readability - -If a specific file already follows a local style, adhere to existing file conventions. - ---- - -## Zero Pragma Policy (Critical) - -**No `#pragma warning disable`** in production code. - -* **StyleCop warnings (SA****) must be fixed**, never suppressed. -* **CA**** warnings may be suppressed only as a last resort** using `[SuppressMessage]` with clear justification. - -Example: - -```csharp -// WRONG -#pragma warning disable CA1062 -public void MyMethod(object parameter) -{ - parameter.ToString(); -} -#pragma warning restore CA1062 - -// CORRECT -public void MyMethod(object parameter) -{ - ArgumentNullException.ThrowIfNull(parameter); - parameter.ToString(); -} - -// LAST RESORT ONLY -[SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", - Justification = "TUnit guarantees non-null parameters from data sources.")] -public async Task MyTest(IConverter converter, int expectedValue) -{ - var result = converter.GetValue(); - await Assert.That(result).IsEqualTo(expectedValue); -} -``` - ---- - -## Testing Guidelines - -* Use TUnit + Microsoft Testing Platform -* Write unit tests for new features and bug fixes -* Prefer existing patterns in: - - * `src/tests/ReactiveUI.Tests/` - * `src/tests/ReactiveUI.AOTTests/` -* Use `ReactiveUI.Testing` utilities for reactive code - ---- - -## Common Development Patterns - -### ViewModel Skeleton - -```csharp -public class SampleViewModel : ReactiveObject -{ - private string? _name; - private readonly ObservableAsPropertyHelper _isValid; - - public SampleViewModel() - { - _isValid = this.WhenAnyValue(x => x.Name) - .Select(name => !string.IsNullOrWhiteSpace(name)) - .ToProperty(this, nameof(IsValid)); - - SubmitCommand = ReactiveCommand.CreateFromTask( - ExecuteSubmit, - this.WhenAnyValue(x => x.IsValid)); - } - - public string? Name - { - get => _name; - set => this.RaiseAndSetIfChanged(ref _name, value); - } - - public bool IsValid => _isValid.Value; - - public ReactiveCommand SubmitCommand { get; } - - private async Task ExecuteSubmit(CancellationToken cancellationToken) - { - // Implementation - } -} -``` - -### RxSchedulers (Preferred) - -```csharp -public IObservable GetData() -{ - return Observable.Return("data") - .ObserveOn(RxSchedulers.MainThreadScheduler); -} -``` - -### WhenAnyValue - -```csharp -this.WhenAnyValue( - x => x.FirstName, - x => x.LastName, - (first, last) => $"{first} {last}") - .Subscribe(fullName => { /* handle */ }); - -this.WhenAnyValue(x => x.IsLoading) - .Where(isLoading => !isLoading) - .Subscribe(_ => { /* handle */ }); -``` - -### ObservableAsPropertyHelper - -```csharp -private readonly ObservableAsPropertyHelper _total; -public decimal Total => _total.Value; - -_total = this.WhenAnyValue( - x => x.Quantity, - x => x.Price, - (qty, price) => qty * price) - .ToProperty(this, nameof(Total)); -``` - ---- - -## What to Avoid - -* Reflection-heavy implementations in core paths -* Expression trees in hot paths without caching -* Platform-specific code in `src/ReactiveUI/` core library -* Breaking public APIs without proper versioning and documentation +Follow CLAUDE.md. If anything elsewhere conflicts with it, CLAUDE.md wins. diff --git a/src/.nuget/NuGet.Config b/src/.nuget/NuGet.Config index 3f0e003403..2c70539053 100644 --- a/src/.nuget/NuGet.Config +++ b/src/.nuget/NuGet.Config @@ -1,4 +1,4 @@ - + diff --git a/src/Benchmarks/AutoPersistBenchmark.cs b/src/Benchmarks/AutoPersistBenchmark.cs index 5102de925a..eb6a5d54d8 100644 --- a/src/Benchmarks/AutoPersistBenchmark.cs +++ b/src/Benchmarks/AutoPersistBenchmark.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/CreateReactiveListBenchmark.cs b/src/Benchmarks/CreateReactiveListBenchmark.cs index bb977ee386..c85f19dc0f 100644 --- a/src/Benchmarks/CreateReactiveListBenchmark.cs +++ b/src/Benchmarks/CreateReactiveListBenchmark.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/Directory.Packages.props b/src/Benchmarks/Directory.Packages.props index 07b47917b5..a5db993d5d 100644 --- a/src/Benchmarks/Directory.Packages.props +++ b/src/Benchmarks/Directory.Packages.props @@ -1,4 +1,4 @@ - + true true diff --git a/src/Benchmarks/INPCObservableForPropertyBenchmarks.cs b/src/Benchmarks/INPCObservableForPropertyBenchmarks.cs index 2f1050a6b4..4439410fee 100644 --- a/src/Benchmarks/INPCObservableForPropertyBenchmarks.cs +++ b/src/Benchmarks/INPCObservableForPropertyBenchmarks.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/Mocks/MockHostScreen.cs b/src/Benchmarks/Mocks/MockHostScreen.cs index 4b1b973ce1..d5b7dd52d7 100644 --- a/src/Benchmarks/Mocks/MockHostScreen.cs +++ b/src/Benchmarks/Mocks/MockHostScreen.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/Mocks/MockViewModel.cs b/src/Benchmarks/Mocks/MockViewModel.cs index 39e1e711c0..ed89ad3d51 100644 --- a/src/Benchmarks/Mocks/MockViewModel.cs +++ b/src/Benchmarks/Mocks/MockViewModel.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/NavigationStackBenchmark.cs b/src/Benchmarks/NavigationStackBenchmark.cs index 3794e8a2c1..b69fa776d6 100644 --- a/src/Benchmarks/NavigationStackBenchmark.cs +++ b/src/Benchmarks/NavigationStackBenchmark.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/Program.cs b/src/Benchmarks/Program.cs index 46e2a58b52..fb5ff031f4 100644 --- a/src/Benchmarks/Program.cs +++ b/src/Benchmarks/Program.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/ReactiveCommandCreateBenchmark.cs b/src/Benchmarks/ReactiveCommandCreateBenchmark.cs index 061de8e35b..13d988c684 100644 --- a/src/Benchmarks/ReactiveCommandCreateBenchmark.cs +++ b/src/Benchmarks/ReactiveCommandCreateBenchmark.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/ReactiveListOperationBenchmark.cs b/src/Benchmarks/ReactiveListOperationBenchmark.cs index 9ac057ef8e..3dc3bf844e 100644 --- a/src/Benchmarks/ReactiveListOperationBenchmark.cs +++ b/src/Benchmarks/ReactiveListOperationBenchmark.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/ReactiveUI.Benchmarks.sln b/src/Benchmarks/ReactiveUI.Benchmarks.sln index 879672676d..04788721e1 100644 --- a/src/Benchmarks/ReactiveUI.Benchmarks.sln +++ b/src/Benchmarks/ReactiveUI.Benchmarks.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27703.2000 diff --git a/src/Benchmarks/RoutableViewModelMixinsBenchmarks.cs b/src/Benchmarks/RoutableViewModelMixinsBenchmarks.cs index 255f1d5dbc..87cf3b81b0 100644 --- a/src/Benchmarks/RoutableViewModelMixinsBenchmarks.cs +++ b/src/Benchmarks/RoutableViewModelMixinsBenchmarks.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/Benchmarks/xunit.runner.json b/src/Benchmarks/xunit.runner.json index 34b2fe2cdd..78c070e832 100644 --- a/src/Benchmarks/xunit.runner.json +++ b/src/Benchmarks/xunit.runner.json @@ -1,3 +1,3 @@ -{ +{ "shadowCopy": false } \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c7a41943b3..3e60da2150 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -16,8 +16,9 @@ https://github.com/reactiveui/ReactiveUI/releases https://github.com/reactiveui/reactiveui git - $(NoWarn);IDE0060;IDE1006;IDE0130;VSSpell001;CA1510 - + true + enable + true @@ -27,9 +28,14 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb enable - preview - True + + latest + + true latest + true + true + nullable;CS4014 true true @@ -44,7 +50,6 @@ true true true - $(NoWarn);CS4014 @@ -52,7 +57,7 @@ net8.0;net9.0;net10.0 - net462;net472;net481 + net462;net472;net481 net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 @@ -64,7 +69,7 @@ $(ReactiveUIFrameworkTargets);$(ReactiveUIWindowsTargets) net8.0;net9.0;net10.0 - net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 $(ReactiveUIMauiTestTargets);net9.0;net10.0 @@ -81,7 +86,7 @@ $(ReactiveUITestingTargets) - net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 $(ReactiveUICoreTargets) @@ -89,8 +94,8 @@ $(ReactiveUIFinalTargetFrameworks);$(ReactiveUIAndroidTargets) - - $(ReactiveUIFinalTargetFrameworks);$(ReactiveUIFrameworkTargets);$(ReactiveUIWindowsTargets) + + $(ReactiveUIFinalTargetFrameworks);$(ReactiveUIFrameworkTargets);$(ReactiveUIWindowsTargets) $(ReactiveUIFinalTargetFrameworks);$(ReactiveUIAppleTargets) @@ -144,10 +149,21 @@ + + + minor + alpha.0 + 23.2 + + - + + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index d368f1fe76..d3cf5e8c43 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -1,109 +1,111 @@ - + true true 19.3.1 - 1.24.18 + 1.45.29 1.17.0 2.10.0.2 - 10.0.51 + 10.0.70 9.0.120 1.1.142 1.1.142 1.1.142 1.1.142 - 10.0.5 - 9.0.14 - 8.0.25 + 10.0.8 + 9.0.16 + 8.0.27 3.1.32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - + + + + + + + - + - + - + - - - - + + + + - - - - - - - - - + + + + + + + + + diff --git a/src/ReactiveUI.AndroidX/Builder/AndroidXReactiveUIBuilderExtensions.cs b/src/ReactiveUI.AndroidX/Builder/AndroidXReactiveUIBuilderExtensions.cs index 53ea1f6608..18299636ad 100644 --- a/src/ReactiveUI.AndroidX/Builder/AndroidXReactiveUIBuilderExtensions.cs +++ b/src/ReactiveUI.AndroidX/Builder/AndroidXReactiveUIBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.AndroidX/ControlFetcherMixin.cs b/src/ReactiveUI.AndroidX/ControlFetcherMixin.cs index d8fc49dd3b..9e4c5cc608 100644 --- a/src/ReactiveUI.AndroidX/ControlFetcherMixin.cs +++ b/src/ReactiveUI.AndroidX/ControlFetcherMixin.cs @@ -1,12 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.Views; - using static ReactiveUI.ControlFetcherMixin; - using Fragment = AndroidX.Fragment.App.Fragment; namespace ReactiveUI.AndroidX; @@ -18,6 +16,20 @@ namespace ReactiveUI.AndroidX; /// public static class ControlFetcherMixin { + /// + /// Wires a control to a property using the strategy. + /// This should be called in the Fragment's OnCreateView, with the newly inflated layout. + /// + /// The fragment. + /// The inflated view. + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] + [RequiresDynamicCode("Android resource discovery uses reflection that may require dynamic code generation.")] + public static void WireUpControls( + this Fragment fragment, + View inflatedView) => + fragment.WireUpControls(inflatedView, ResolveStrategy.Implicit); + /// /// Wires a control to a property. /// This should be called in the Fragment's OnCreateView, with the newly inflated layout. @@ -25,9 +37,13 @@ public static class ControlFetcherMixin /// The fragment. /// The inflated view. /// The resolve members. - [RequiresUnreferencedCode("Android resource discovery uses reflection over generated resource types that may be trimmed.")] + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] [RequiresDynamicCode("Android resource discovery uses reflection that may require dynamic code generation.")] - public static void WireUpControls(this Fragment fragment, View inflatedView, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) + public static void WireUpControls( + this Fragment fragment, + View inflatedView, + ResolveStrategy resolveMembers) { ArgumentExceptionHelper.ThrowIfNull(fragment); @@ -43,8 +59,9 @@ public static void WireUpControls(this Fragment fragment, View inflatedView, Res } catch (Exception ex) { - throw new - MissingFieldException("Failed to wire up the Property " + member.Name + " to a View in your layout with a corresponding identifier", ex); + throw new MissingFieldException( + "Failed to wire up the Property " + member.Name + " to a View in your layout with a corresponding identifier", + ex); } } } diff --git a/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity.cs b/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity.cs index 6e1e835e1c..cb40f2a72c 100644 --- a/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity.cs +++ b/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -6,7 +6,6 @@ using Android.App; using Android.Content; using Android.Runtime; - using AndroidX.AppCompat.App; namespace ReactiveUI.AndroidX; @@ -15,10 +14,22 @@ namespace ReactiveUI.AndroidX; /// This is an Activity that is both an Activity and has ReactiveObject powers /// (i.e. you can call RaiseAndSetIfChanged). /// -public class ReactiveAppCompatActivity : AppCompatActivity, IReactiveObject, IReactiveNotifyPropertyChanged, IHandleObservableErrors +public class ReactiveAppCompatActivity : AppCompatActivity, IReactiveObject, + IReactiveNotifyPropertyChanged, IHandleObservableErrors { + /// + /// The subject that signals when the activity is activated. + /// private readonly Subject _activated = new(); + + /// + /// The subject that signals when the activity is deactivated. + /// private readonly Subject _deactivated = new(); + + /// + /// The subject that signals activity results. + /// private readonly Subject<(int requestCode, Result result, Intent? intent)> _activityResult = new(); /// @@ -45,10 +56,12 @@ protected ReactiveAppCompatActivity(in IntPtr handle, JniHandleOwnership ownersh public event PropertyChangedEventHandler? PropertyChanged; /// - public IObservable> Changing => this.GetChangingObservable(); + public IObservable> Changing => + this.GetChangingObservable(); /// - public IObservable> Changed => this.GetChangedObservable(); + public IObservable> Changed => + this.GetChangedObservable(); /// public IObservable ThrownExceptions => this.GetThrownExceptionsObservable(); @@ -75,7 +88,8 @@ protected ReactiveAppCompatActivity(in IntPtr handle, JniHandleOwnership ownersh /// /// The activity result. /// - public IObservable<(int requestCode, Result result, Intent? intent)> ActivityResult => _activityResult.AsObservable(); + public IObservable<(int requestCode, Result result, Intent? intent)> ActivityResult => + _activityResult.AsObservable(); /// void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => PropertyChanging?.Invoke(this, args); @@ -97,10 +111,10 @@ protected ReactiveAppCompatActivity(in IntPtr handle, JniHandleOwnership ownersh // NB: It's important that we set up the subscription *before* we // call ActivityForResult var ret = ActivityResult - .Where(x => x.requestCode == requestCode) - .Select(x => (x.result, x.intent)) - .FirstAsync() - .ToTask(); + .Where(x => x.requestCode == requestCode) + .Select(x => (x.result, x.intent)) + .FirstAsync() + .ToTask(); StartActivityForResult(intent, requestCode); return ret; @@ -117,10 +131,10 @@ protected ReactiveAppCompatActivity(in IntPtr handle, JniHandleOwnership ownersh // NB: It's important that we set up the subscription *before* we // call ActivityForResult var ret = ActivityResult - .Where(x => x.requestCode == requestCode) - .Select(x => (x.result, x.intent)) - .FirstAsync() - .ToTask(); + .Where(x => x.requestCode == requestCode) + .Select(x => (x.result, x.intent)) + .FirstAsync() + .ToTask(); StartActivityForResult(type, requestCode); return ret; diff --git a/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity{TViewModel}.cs b/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity{TViewModel}.cs index 79e0ff1cb6..ae3bb7847d 100644 --- a/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity{TViewModel}.cs +++ b/src/ReactiveUI.AndroidX/ReactiveAppCompatActivity{TViewModel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,6 +13,9 @@ namespace ReactiveUI.AndroidX; public class ReactiveAppCompatActivity : ReactiveAppCompatActivity, IViewFor, ICanActivate where TViewModel : class { + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// diff --git a/src/ReactiveUI.AndroidX/ReactiveDialogFragment.cs b/src/ReactiveUI.AndroidX/ReactiveDialogFragment.cs index 7369e4a3c1..bdc1fb89e2 100644 --- a/src/ReactiveUI.AndroidX/ReactiveDialogFragment.cs +++ b/src/ReactiveUI.AndroidX/ReactiveDialogFragment.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -9,9 +9,17 @@ namespace ReactiveUI.AndroidX; /// This is a Fragment that is both an Activity and has ReactiveObject powers /// (i.e. you can call RaiseAndSetIfChanged). /// -public class ReactiveDialogFragment : global::AndroidX.Fragment.App.DialogFragment, IReactiveNotifyPropertyChanged, IReactiveObject, IHandleObservableErrors +public class ReactiveDialogFragment : global::AndroidX.Fragment.App.DialogFragment, + IReactiveNotifyPropertyChanged, IReactiveObject, IHandleObservableErrors { + /// + /// The subject that signals when the fragment is activated. + /// private readonly Subject _activated = new(); + + /// + /// The subject that signals when the fragment is deactivated. + /// private readonly Subject _deactivated = new(); /// @@ -41,10 +49,12 @@ protected ReactiveDialogFragment() public IObservable Deactivated => _deactivated.AsObservable(); /// - public IObservable> Changing => this.GetChangingObservable(); + public IObservable> Changing => + this.GetChangingObservable(); /// - public IObservable> Changed => this.GetChangedObservable(); + public IObservable> Changed => + this.GetChangedObservable(); /// void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => PropertyChanging?.Invoke(this, args); diff --git a/src/ReactiveUI.AndroidX/ReactiveDialogFragment{TViewModel}.cs b/src/ReactiveUI.AndroidX/ReactiveDialogFragment{TViewModel}.cs index cec0a91d52..9479d43f65 100644 --- a/src/ReactiveUI.AndroidX/ReactiveDialogFragment{TViewModel}.cs +++ b/src/ReactiveUI.AndroidX/ReactiveDialogFragment{TViewModel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,6 +13,9 @@ namespace ReactiveUI.AndroidX; public class ReactiveDialogFragment : ReactiveDialogFragment, IViewFor, ICanActivate where TViewModel : class { + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// diff --git a/src/ReactiveUI.AndroidX/ReactiveFragment.cs b/src/ReactiveUI.AndroidX/ReactiveFragment.cs index d0bd94455b..eafbecd1d0 100644 --- a/src/ReactiveUI.AndroidX/ReactiveFragment.cs +++ b/src/ReactiveUI.AndroidX/ReactiveFragment.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -10,9 +10,17 @@ namespace ReactiveUI.AndroidX; /// (i.e. you can call RaiseAndSetIfChanged). /// [ExcludeFromCodeCoverage] -public class ReactiveFragment : global::AndroidX.Fragment.App.Fragment, IReactiveNotifyPropertyChanged, IReactiveObject, IHandleObservableErrors +public class ReactiveFragment : global::AndroidX.Fragment.App.Fragment, + IReactiveNotifyPropertyChanged, IReactiveObject, IHandleObservableErrors { + /// + /// The subject that signals when the fragment is activated. + /// private readonly Subject _activated = new(); + + /// + /// The subject that signals when the fragment is deactivated. + /// private readonly Subject _deactivated = new(); /// diff --git a/src/ReactiveUI.AndroidX/ReactiveFragmentActivity.cs b/src/ReactiveUI.AndroidX/ReactiveFragmentActivity.cs index 909a5a939a..e08ba38af6 100644 --- a/src/ReactiveUI.AndroidX/ReactiveFragmentActivity.cs +++ b/src/ReactiveUI.AndroidX/ReactiveFragmentActivity.cs @@ -1,11 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.App; using Android.Content; - using AndroidX.Fragment.App; namespace ReactiveUI.AndroidX; @@ -14,10 +13,22 @@ namespace ReactiveUI.AndroidX; /// This is an Activity that is both an Activity and has ReactiveObject powers /// (i.e. you can call RaiseAndSetIfChanged). /// -public class ReactiveFragmentActivity : FragmentActivity, IReactiveObject, IReactiveNotifyPropertyChanged, IHandleObservableErrors +public class ReactiveFragmentActivity : FragmentActivity, IReactiveObject, + IReactiveNotifyPropertyChanged, IHandleObservableErrors { + /// + /// The subject that signals when the activity is activated. + /// private readonly Subject _activated = new(); + + /// + /// The subject that signals when the activity is deactivated. + /// private readonly Subject _deactivated = new(); + + /// + /// The subject that signals activity results. + /// private readonly Subject<(int requestCode, Result result, Intent intent)> _activityResult = new(); /// @@ -27,10 +38,12 @@ public class ReactiveFragmentActivity : FragmentActivity, IReactiveObject, IReac public event PropertyChangedEventHandler? PropertyChanged; /// - public IObservable> Changing => this.GetChangingObservable(); + public IObservable> Changing => + this.GetChangingObservable(); /// - public IObservable> Changed => this.GetChangedObservable(); + public IObservable> Changed => + this.GetChangedObservable(); /// public IObservable ThrownExceptions => this.GetThrownExceptionsObservable(); @@ -48,7 +61,8 @@ public class ReactiveFragmentActivity : FragmentActivity, IReactiveObject, IReac /// /// Gets the activity result. /// - public IObservable<(int requestCode, Result result, Intent intent)> ActivityResult => _activityResult.AsObservable(); + public IObservable<(int requestCode, Result result, Intent intent)> ActivityResult => + _activityResult.AsObservable(); /// void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => PropertyChanging?.Invoke(this, args); @@ -70,10 +84,10 @@ public class ReactiveFragmentActivity : FragmentActivity, IReactiveObject, IReac // NB: It's important that we set up the subscription *before* we // call ActivityForResult var ret = ActivityResult - .Where(x => x.requestCode == requestCode) - .Select(x => (x.result, x.intent)) - .FirstAsync() - .ToTask(); + .Where(x => x.requestCode == requestCode) + .Select(x => (x.result, x.intent)) + .FirstAsync() + .ToTask(); StartActivityForResult(intent, requestCode); return ret; @@ -90,10 +104,10 @@ public class ReactiveFragmentActivity : FragmentActivity, IReactiveObject, IReac // NB: It's important that we set up the subscription *before* we // call ActivityForResult var ret = ActivityResult - .Where(x => x.requestCode == requestCode) - .Select(x => (x.result, x.intent)) - .FirstAsync() - .ToTask(); + .Where(x => x.requestCode == requestCode) + .Select(x => (x.result, x.intent)) + .FirstAsync() + .ToTask(); StartActivityForResult(type, requestCode); return ret; diff --git a/src/ReactiveUI.AndroidX/ReactiveFragmentActivity{TViewModel}.cs b/src/ReactiveUI.AndroidX/ReactiveFragmentActivity{TViewModel}.cs index 85c8609875..beebef6601 100644 --- a/src/ReactiveUI.AndroidX/ReactiveFragmentActivity{TViewModel}.cs +++ b/src/ReactiveUI.AndroidX/ReactiveFragmentActivity{TViewModel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,6 +13,9 @@ namespace ReactiveUI.AndroidX; public class ReactiveFragmentActivity : ReactiveFragmentActivity, IViewFor, ICanActivate where TViewModel : class { + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// diff --git a/src/ReactiveUI.AndroidX/ReactiveFragment{TViewModel}.cs b/src/ReactiveUI.AndroidX/ReactiveFragment{TViewModel}.cs index 9400a30eeb..eb8634984a 100644 --- a/src/ReactiveUI.AndroidX/ReactiveFragment{TViewModel}.cs +++ b/src/ReactiveUI.AndroidX/ReactiveFragment{TViewModel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,6 +13,9 @@ namespace ReactiveUI.AndroidX; public class ReactiveFragment : ReactiveFragment, IViewFor, ICanActivate where TViewModel : class { + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// diff --git a/src/ReactiveUI.AndroidX/ReactivePagerAdapter.cs b/src/ReactiveUI.AndroidX/ReactivePagerAdapter.cs index c9b00220a4..7a919e3ec7 100644 --- a/src/ReactiveUI.AndroidX/ReactivePagerAdapter.cs +++ b/src/ReactiveUI.AndroidX/ReactivePagerAdapter.cs @@ -1,14 +1,11 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.Views; - using AndroidX.ViewPager.Widget; - using DynamicData; - using Object = Java.Lang.Object; namespace ReactiveUI.AndroidX; @@ -21,11 +18,30 @@ namespace ReactiveUI.AndroidX; public class ReactivePagerAdapter : PagerAdapter, IEnableLogger where TViewModel : class { + /// The backing list of view models that drives the adapter. private readonly SourceList _list; + + /// Creates the view for a given view model. private readonly Func _viewCreator; + + /// Optional initializer invoked for each created view. private readonly Action? _viewInitializer; + + /// Subscription that keeps the adapter in sync with the change set. private readonly IDisposable _inner; + /// + /// Initializes a new instance of the class. + /// + /// The change set to page. + /// A function which will create the view. + public ReactivePagerAdapter( + IObservable> changeSet, + Func viewCreator) + : this(changeSet, viewCreator, null) + { + } + /// /// Initializes a new instance of the class. /// @@ -35,9 +51,9 @@ public class ReactivePagerAdapter : PagerAdapter, IEnableLogger public ReactivePagerAdapter( IObservable> changeSet, Func viewCreator, - Action? viewInitializer = null) + Action? viewInitializer) { - _list = new SourceList(changeSet); + _list = new(changeSet); _viewCreator = viewCreator; _viewInitializer = viewInitializer; @@ -48,7 +64,7 @@ public ReactivePagerAdapter( public override int Count => _list.Count; /// - public override bool IsViewFromObject(View view, Object @object) => (View)@object == view; + public override bool IsViewFromObject(View view, Object @object) => view.Equals(@object); /// public override Object InstantiateItem(ViewGroup container, int position) diff --git a/src/ReactiveUI.AndroidX/ReactivePagerAdapter{TViewModel,TCollection}.cs b/src/ReactiveUI.AndroidX/ReactivePagerAdapter{TViewModel,TCollection}.cs index 3760709550..83e64fa3e2 100644 --- a/src/ReactiveUI.AndroidX/ReactivePagerAdapter{TViewModel,TCollection}.cs +++ b/src/ReactiveUI.AndroidX/ReactivePagerAdapter{TViewModel,TCollection}.cs @@ -1,13 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Specialized; - using Android.Views; - -using DynamicData; using DynamicData.Binding; namespace ReactiveUI.AndroidX; @@ -27,6 +24,10 @@ namespace ReactiveUI.AndroidX; public class ReactivePagerAdapter( TCollection collection, Func viewCreator, - Action? viewInitializer = null) : ReactivePagerAdapter(collection.ToObservableChangeSet(), viewCreator, viewInitializer) + Action? viewInitializer = null) + : ReactivePagerAdapter( + collection.ToObservableChangeSet(), + viewCreator, + viewInitializer) where TViewModel : class where TCollection : INotifyCollectionChanged, IEnumerable; diff --git a/src/ReactiveUI.AndroidX/ReactivePreferenceFragment.cs b/src/ReactiveUI.AndroidX/ReactivePreferenceFragment.cs index db101a1dc2..69f886bd65 100644 --- a/src/ReactiveUI.AndroidX/ReactivePreferenceFragment.cs +++ b/src/ReactiveUI.AndroidX/ReactivePreferenceFragment.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.Runtime; - using AndroidX.Preference; namespace ReactiveUI.AndroidX; @@ -13,9 +12,17 @@ namespace ReactiveUI.AndroidX; /// This is a PreferenceFragment that is both an Activity and has ReactiveObject powers /// (i.e. you can call RaiseAndSetIfChanged). /// -public abstract class ReactivePreferenceFragment : PreferenceFragmentCompat, IReactiveNotifyPropertyChanged, IReactiveObject, IHandleObservableErrors +public abstract class ReactivePreferenceFragment : PreferenceFragmentCompat, + IReactiveNotifyPropertyChanged, IReactiveObject, IHandleObservableErrors { + /// + /// The subject that signals when the fragment is activated. + /// private readonly Subject _activated = new(); + + /// + /// The subject that signals when the fragment is deactivated. + /// private readonly Subject _deactivated = new(); /// @@ -42,10 +49,12 @@ protected ReactivePreferenceFragment(in IntPtr handle, JniHandleOwnership owners public event PropertyChangedEventHandler? PropertyChanged; /// - public IObservable> Changing => this.GetChangingObservable(); + public IObservable> Changing => + this.GetChangingObservable(); /// - public IObservable> Changed => this.GetChangedObservable(); + public IObservable> Changed => + this.GetChangedObservable(); /// public IObservable ThrownExceptions => this.GetThrownExceptionsObservable(); diff --git a/src/ReactiveUI.AndroidX/ReactivePreferenceFragment{TViewModel}.cs b/src/ReactiveUI.AndroidX/ReactivePreferenceFragment{TViewModel}.cs index 02bfda82e8..3f55fa760e 100644 --- a/src/ReactiveUI.AndroidX/ReactivePreferenceFragment{TViewModel}.cs +++ b/src/ReactiveUI.AndroidX/ReactivePreferenceFragment{TViewModel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -12,9 +12,13 @@ namespace ReactiveUI.AndroidX; /// (i.e. you can call RaiseAndSetIfChanged). /// /// The view model type. -public abstract class ReactivePreferenceFragment : ReactivePreferenceFragment, IViewFor, ICanActivate +public abstract class ReactivePreferenceFragment : ReactivePreferenceFragment, IViewFor, + ICanActivate where TViewModel : class { + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// diff --git a/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter.cs b/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter.cs index e9ffdf2d14..f7255889f7 100644 --- a/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter.cs +++ b/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using AndroidX.RecyclerView.Widget; - using DynamicData; namespace ReactiveUI.AndroidX; @@ -16,8 +15,14 @@ namespace ReactiveUI.AndroidX; public abstract class ReactiveRecyclerViewAdapter : RecyclerView.Adapter where TViewModel : class, IReactiveObject { + /// + /// The source list that backs the adapter. + /// private readonly SourceList _list; + /// + /// The subscription that keeps the bindings in sync with the source list. + /// private readonly IDisposable _inner; /// @@ -26,12 +31,12 @@ public abstract class ReactiveRecyclerViewAdapter : RecyclerView.Ada /// The backing list. protected ReactiveRecyclerViewAdapter(IObservable> backingList) { - _list = new SourceList(backingList); + _list = new(backingList); _inner = _list - .Connect() - .ForEachChange(UpdateBindings) - .Subscribe(); + .Connect() + .ForEachChange(UpdateBindings) + .Subscribe(); } /// @@ -74,32 +79,58 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + /// + /// Gets the view model at the specified position, or null if the position is out of range. + /// + /// The position in the list. + /// The view model at the position, or null. private TViewModel? GetViewModelByPosition(int position) => position >= _list.Count ? null : _list.Items[position]; + /// + /// Updates the adapter bindings in response to a change in the source list. + /// + /// The change to apply. private void UpdateBindings(Change change) { switch (change.Reason) { case ListChangeReason.Add: - NotifyItemInserted(change.Item.CurrentIndex); - break; + { + NotifyItemInserted(change.Item.CurrentIndex); + break; + } + case ListChangeReason.Remove: - NotifyItemRemoved(change.Item.CurrentIndex); - break; + { + NotifyItemRemoved(change.Item.CurrentIndex); + break; + } + case ListChangeReason.Moved: - NotifyItemMoved(change.Item.PreviousIndex, change.Item.CurrentIndex); - break; + { + NotifyItemMoved(change.Item.PreviousIndex, change.Item.CurrentIndex); + break; + } + case ListChangeReason.Replace: case ListChangeReason.Refresh: - NotifyItemChanged(change.Item.CurrentIndex); - break; + { + NotifyItemChanged(change.Item.CurrentIndex); + break; + } + case ListChangeReason.AddRange: - NotifyItemRangeInserted(change.Range.Index, change.Range.Count); - break; + { + NotifyItemRangeInserted(change.Range.Index, change.Range.Count); + break; + } + case ListChangeReason.RemoveRange: case ListChangeReason.Clear: - NotifyItemRangeRemoved(change.Range.Index, change.Range.Count); - break; + { + NotifyItemRangeRemoved(change.Range.Index, change.Range.Count); + break; + } } } } diff --git a/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter{TViewModel,TCollection}.cs b/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter{TViewModel,TCollection}.cs index d4c91d6865..ed6dcdc682 100644 --- a/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter{TViewModel,TCollection}.cs +++ b/src/ReactiveUI.AndroidX/ReactiveRecyclerViewAdapter{TViewModel,TCollection}.cs @@ -1,13 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Specialized; - using AndroidX.RecyclerView.Widget; - -using DynamicData; using DynamicData.Binding; namespace ReactiveUI.AndroidX; @@ -21,6 +18,7 @@ namespace ReactiveUI.AndroidX; /// Initializes a new instance of the class. /// /// The backing list. -public abstract class ReactiveRecyclerViewAdapter(TCollection backingList) : ReactiveRecyclerViewAdapter(backingList.ToObservableChangeSet()) +public abstract class ReactiveRecyclerViewAdapter(TCollection backingList) + : ReactiveRecyclerViewAdapter(backingList.ToObservableChangeSet()) where TViewModel : class, IReactiveObject where TCollection : ICollection, INotifyCollectionChanged; diff --git a/src/ReactiveUI.AndroidX/ReactiveRecyclerViewViewHolder.cs b/src/ReactiveUI.AndroidX/ReactiveRecyclerViewViewHolder.cs index e48d700d07..a8143de4b1 100644 --- a/src/ReactiveUI.AndroidX/ReactiveRecyclerViewViewHolder.cs +++ b/src/ReactiveUI.AndroidX/ReactiveRecyclerViewViewHolder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -6,9 +6,7 @@ using System.Reflection; using System.Runtime.Serialization; using System.Text.Json.Serialization; - using Android.Views; - using AndroidX.RecyclerView.Widget; namespace ReactiveUI.AndroidX; @@ -17,24 +15,39 @@ namespace ReactiveUI.AndroidX; /// A implementation that binds to a reactive view model. /// /// The type of the view model. -[RequiresUnreferencedCode("Android property discovery uses reflection over generated resource types that may be trimmed.")] +[RequiresUnreferencedCode( + "Android property discovery uses reflection over generated resource types that may be trimmed.")] [RequiresDynamicCode("Android property discovery discovery uses reflection that may require dynamic code generation.")] -public class ReactiveRecyclerViewViewHolder : RecyclerView.ViewHolder, ILayoutViewHost, IViewFor, IReactiveNotifyPropertyChanged>, IReactiveObject, ICanActivate +public class ReactiveRecyclerViewViewHolder : RecyclerView.ViewHolder, ILayoutViewHost, + IViewFor, IReactiveNotifyPropertyChanged>, IReactiveObject, + ICanActivate where TViewModel : class, IReactiveObject { /// /// Gets all public accessible properties. /// - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401: Field should be private", Justification = "Legacy reasons")] + [SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1401: Field should be private", + Justification = "Legacy reasons")] [SuppressMessage("Design", "CA1051: Do not declare visible instance fields", Justification = "Legacy reasons")] [IgnoreDataMember] [JsonIgnore] protected Lazy? AllPublicProperties; + /// + /// The subject that signals when the view is activated. + /// private readonly Subject _activated = new(); + /// + /// The subject that signals when the view is deactivated. + /// private readonly Subject _deactivated = new(); + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// @@ -159,12 +172,14 @@ public TViewModel? ViewModel /// [IgnoreDataMember] [JsonIgnore] - public IObservable>> Changing => this.GetChangingObservable(); + public IObservable>> Changing => + this.GetChangingObservable(); /// [IgnoreDataMember] [JsonIgnore] - public IObservable>> Changed => this.GetChangedObservable(); + public IObservable>> Changed => + this.GetChangedObservable(); /// public IDisposable SuppressChangeNotifications() => IReactiveObjectExtensions.SuppressChangeNotifications(this); @@ -196,13 +211,32 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + /// + /// Sets up the reactive object after deserialization. + /// + /// The streaming context. [OnDeserialized] private void SetupRxObj(in StreamingContext sc) => SetupRxObj(); + /// + /// Sets up the reactive object by initializing the public property cache. + /// private void SetupRxObj() => - AllPublicProperties = new Lazy(() => [.. GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)]); + AllPublicProperties = new(() => [.. GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)]); - private void OnViewAttachedToWindow(object? sender, View.ViewAttachedToWindowEventArgs args) => _activated.OnNext(Unit.Default); + /// + /// Handles the view being attached to the window and signals activation. + /// + /// The source of the event. + /// The event arguments. + private void OnViewAttachedToWindow(object? sender, View.ViewAttachedToWindowEventArgs args) => + _activated.OnNext(Unit.Default); - private void OnViewDetachedFromWindow(object? sender, View.ViewDetachedFromWindowEventArgs args) => _deactivated.OnNext(Unit.Default); + /// + /// Handles the view being detached from the window and signals deactivation. + /// + /// The source of the event. + /// The event arguments. + private void OnViewDetachedFromWindow(object? sender, View.ViewDetachedFromWindowEventArgs args) => + _deactivated.OnNext(Unit.Default); } diff --git a/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj b/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj index b32e61ffc5..f5d26ab97a 100644 --- a/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj +++ b/src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIAndroidTargets) Provides ReactiveUI extensions for .NET Android applications using AndroidX libraries @@ -7,31 +7,31 @@ 34.0 - - - - - - - - - - - - + + + + + + + + + + + + - + - - + + - + - + - \ No newline at end of file + diff --git a/src/ReactiveUI.AndroidX/Registrations.cs b/src/ReactiveUI.AndroidX/Registrations.cs index 72e08dd09f..0f0e852cd9 100644 --- a/src/ReactiveUI.AndroidX/Registrations.cs +++ b/src/ReactiveUI.AndroidX/Registrations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -25,9 +25,11 @@ public void Register(IRegistrar registrar) // AndroidX specific registrations could be added here if needed in the future. // Ensure a SynchronizationContext exists on Android when not in unit tests. - if (!ModeDetector.InUnitTestRunner() && Looper.MyLooper() is null) + if (ModeDetector.InUnitTestRunner() || Looper.MyLooper() is not null) { - Looper.Prepare(); + return; } + + Looper.Prepare(); } } diff --git a/src/ReactiveUI.Blazor/Builder/BlazorReactiveUIBuilderExtensions.cs b/src/ReactiveUI.Blazor/Builder/BlazorReactiveUIBuilderExtensions.cs index 073d036d5b..69fcefa80a 100644 --- a/src/ReactiveUI.Blazor/Builder/BlazorReactiveUIBuilderExtensions.cs +++ b/src/ReactiveUI.Blazor/Builder/BlazorReactiveUIBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Blazor/GlobalUsings.cs b/src/ReactiveUI.Blazor/GlobalUsings.cs index 531670cdee..da4bf97bde 100644 --- a/src/ReactiveUI.Blazor/GlobalUsings.cs +++ b/src/ReactiveUI.Blazor/GlobalUsings.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Blazor/Internal/ReactiveComponentHelpers.cs b/src/ReactiveUI.Blazor/Internal/ReactiveComponentHelpers.cs index bd4f245526..4ff8f56bb4 100644 --- a/src/ReactiveUI.Blazor/Internal/ReactiveComponentHelpers.cs +++ b/src/ReactiveUI.Blazor/Internal/ReactiveComponentHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -69,7 +69,7 @@ public static IObservable CreatePropertyChangedPulse(INotifyPropertyChange /// When the component is deactivated, the view model's activator is deactivated. /// /// - /// The activation subscription is added to to ensure + /// The activation subscription is added to to ensure /// it is disposed when the component is disposed. The deactivation subscription does not require explicit disposal /// as it is a fire-and-forget operation that completes when the component is disposed. /// @@ -81,7 +81,7 @@ public static IObservable CreatePropertyChangedPulse(INotifyPropertyChange /// /// Thrown when or is . /// - public static void WireActivationIfSupported(T? viewModel, ReactiveComponentState state) + public static void WireActivationIfSupported(T? viewModel, ReactiveComponentState state) where T : class, INotifyPropertyChanged { ArgumentNullException.ThrowIfNull(state); @@ -150,35 +150,36 @@ public static IObservable CreateViewModelChangedStream( ArgumentNullException.ThrowIfNull(removePropertyChangedHandler); ArgumentNullException.ThrowIfNull(viewModelPropertyName); - return Observable.Create( - observer => + return Observable.Create(observer => + { + // Emit current value once to preserve the original "Skip(1)" behavior in consumers + var current = getCurrentViewModel(); + if (current is not null) + { + observer.OnNext(current); + } + + // Handler for subsequent changes + void Handler(object? sender, PropertyChangedEventArgs e) { - // Emit current value once to preserve the original "Skip(1)" behavior in consumers - var current = getCurrentViewModel(); - if (current is not null) + // Use ordinal comparison for best performance; nameof() produces ordinal strings + if (!string.Equals(e.PropertyName, viewModelPropertyName, StringComparison.Ordinal)) { - observer.OnNext(current); + return; } - // Handler for subsequent changes - void Handler(object? sender, PropertyChangedEventArgs e) + var vm = getCurrentViewModel(); + if (vm is null) { - // Use ordinal comparison for best performance; nameof() produces ordinal strings - if (!string.Equals(e.PropertyName, viewModelPropertyName, StringComparison.Ordinal)) - { - return; - } - - var vm = getCurrentViewModel(); - if (vm is not null) - { - observer.OnNext(vm); - } + return; } - addPropertyChangedHandler(Handler); - return Disposable.Create(() => removePropertyChangedHandler(Handler)); - }); + observer.OnNext(vm); + } + + addPropertyChangedHandler(Handler); + return Disposable.Create(() => removePropertyChangedHandler(Handler)); + }); } /// @@ -202,7 +203,7 @@ void Handler(object? sender, PropertyChangedEventArgs e) /// /// /// A disposable that tears down all subscriptions when disposed. Should be assigned to - /// . + /// . /// /// /// diff --git a/src/ReactiveUI.Blazor/Internal/ReactiveComponentState.cs b/src/ReactiveUI.Blazor/Internal/ReactiveComponentState.cs index 29d94f45a8..8c4e7f4f06 100644 --- a/src/ReactiveUI.Blazor/Internal/ReactiveComponentState.cs +++ b/src/ReactiveUI.Blazor/Internal/ReactiveComponentState.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,7 +11,6 @@ namespace ReactiveUI.Blazor.Internal; /// Internal state container for reactive Blazor components. /// Manages activation lifecycle, subscriptions, and disposal semantics. /// -/// The view model type that implements . /// /// /// This class encapsulates the common reactive infrastructure shared across all reactive Blazor component base classes, @@ -22,8 +21,7 @@ namespace ReactiveUI.Blazor.Internal; /// created once per component and reused throughout the component's lifetime. /// /// -internal sealed class ReactiveComponentState : IDisposable - where T : class, INotifyPropertyChanged +internal sealed class ReactiveComponentState : IDisposable { /// /// Signals component activation. Emits when is called. @@ -36,7 +34,10 @@ internal sealed class ReactiveComponentState : IDisposable /// /// Suppressed CA2213 because this subject is used for signaling only and is disposed explicitly in . /// - [SuppressMessage("Design", "CA2213:Disposable fields should be disposed", Justification = "Disposed explicitly in Dispose method.")] + [SuppressMessage( + "Design", + "CA2213:Disposable fields should be disposed", + Justification = "Disposed explicitly in Dispose method.")] private readonly Subject _deactivateSubject = new(); /// @@ -84,7 +85,10 @@ internal sealed class ReactiveComponentState : IDisposable /// Use this to register subscriptions that should live for the entire component lifetime. /// All subscriptions added here will be disposed when the component is disposed. /// - [SuppressMessage("Style", "RCS1085:Use auto-implemented property", Justification = "Explicit field backing provides clarity and follows established pattern in this class.")] + [SuppressMessage( + "Style", + "RCS1085:Use auto-implemented property", + Justification = "Explicit field backing provides clarity and follows established pattern in this class.")] public CompositeDisposable LifetimeDisposables => _lifetimeDisposables; /// @@ -101,7 +105,10 @@ internal sealed class ReactiveComponentState : IDisposable /// ensuring proper disposal semantics. /// /// - [SuppressMessage("Style", "RCS1085:Use auto-implemented property", Justification = "Intentional wrapper for SerialDisposable.Disposable property to ensure proper disposal semantics.")] + [SuppressMessage( + "Style", + "RCS1085:Use auto-implemented property", + Justification = "Intentional wrapper for SerialDisposable.Disposable property to ensure proper disposal semantics.")] public IDisposable? FirstRenderSubscriptions { get => _firstRenderSubscriptions.Disposable; diff --git a/src/ReactiveUI.Blazor/PlatformOperations.cs b/src/ReactiveUI.Blazor/PlatformOperations.cs index 3dd4fd375f..94639aff4e 100644 --- a/src/ReactiveUI.Blazor/PlatformOperations.cs +++ b/src/ReactiveUI.Blazor/PlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Blazor/Properties/launchSettings.json b/src/ReactiveUI.Blazor/Properties/launchSettings.json index c2b2934570..3d2caae2f8 100644 --- a/src/ReactiveUI.Blazor/Properties/launchSettings.json +++ b/src/ReactiveUI.Blazor/Properties/launchSettings.json @@ -1,27 +1,27 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:50286/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:50286/", + "sslPort": 0 + } }, - "ReactiveUI.Blazor": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:50291/" + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "ReactiveUI.Blazor": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:50291/" + } } - } -} \ No newline at end of file +} diff --git a/src/ReactiveUI.Blazor/ReactiveComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveComponentBase.cs index e8dc4ba148..e9505cf15e 100644 --- a/src/ReactiveUI.Blazor/ReactiveComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveComponentBase.cs @@ -1,12 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Runtime.CompilerServices; - using Microsoft.AspNetCore.Components; - using ReactiveUI.Blazor.Internal; namespace ReactiveUI.Blazor; @@ -25,13 +23,14 @@ namespace ReactiveUI.Blazor; /// observables instead. /// /// +[SuppressMessage("Usage", "BL0007:Component parameters should be auto properties", Justification = "Needed for design of the properties")] public class ReactiveComponentBase : ComponentBase, IViewFor, INotifyPropertyChanged, ICanActivate, IDisposable where T : class, INotifyPropertyChanged { /// /// Encapsulates reactive state and lifecycle management for this component. /// - private readonly ReactiveComponentState _state = new(); + private readonly ReactiveComponentState _state = new(); /// /// Backing field for . @@ -119,7 +118,7 @@ protected override void OnAfterRender(bool firstRender) /// /// The name of the changed property. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + PropertyChanged?.Invoke(this, new(propertyName)); /// /// Releases managed resources used by the component. diff --git a/src/ReactiveUI.Blazor/ReactiveInjectableComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveInjectableComponentBase.cs index 79f1ee1f23..8daf5ae729 100644 --- a/src/ReactiveUI.Blazor/ReactiveInjectableComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveInjectableComponentBase.cs @@ -1,12 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Runtime.CompilerServices; - using Microsoft.AspNetCore.Components; - using ReactiveUI.Blazor.Internal; namespace ReactiveUI.Blazor; @@ -28,13 +26,14 @@ namespace ReactiveUI.Blazor; /// The is provided via DI using . /// /// -public class ReactiveInjectableComponentBase : ComponentBase, IViewFor, INotifyPropertyChanged, ICanActivate, IDisposable +public class ReactiveInjectableComponentBase : ComponentBase, IViewFor, INotifyPropertyChanged, ICanActivate, + IDisposable where T : class, INotifyPropertyChanged { /// /// Encapsulates reactive state and lifecycle management for this component. /// - private readonly ReactiveComponentState _state = new(); + private readonly ReactiveComponentState _state = new(); /// /// Backing field for . @@ -122,7 +121,7 @@ protected override void OnAfterRender(bool firstRender) /// /// The name of the changed property. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + PropertyChanged?.Invoke(this, new(propertyName)); /// /// Releases managed resources used by the component. diff --git a/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs index 23e451288e..478d10a7ea 100644 --- a/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveLayoutComponentBase.cs @@ -1,12 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Runtime.CompilerServices; - using Microsoft.AspNetCore.Components; - using ReactiveUI.Blazor.Internal; namespace ReactiveUI.Blazor; @@ -25,13 +23,15 @@ namespace ReactiveUI.Blazor; /// observables instead. /// /// -public class ReactiveLayoutComponentBase : LayoutComponentBase, IViewFor, INotifyPropertyChanged, ICanActivate, IDisposable +[SuppressMessage("Usage", "BL0007:Component parameters should be auto properties", Justification = "Needed for design of the properties")] +public class ReactiveLayoutComponentBase : LayoutComponentBase, IViewFor, INotifyPropertyChanged, ICanActivate, + IDisposable where T : class, INotifyPropertyChanged { /// /// Encapsulates reactive state and lifecycle management for this component. /// - private readonly ReactiveComponentState _state = new(); + private readonly ReactiveComponentState _state = new(); /// /// Backing field for . @@ -118,7 +118,7 @@ protected override void OnAfterRender(bool firstRender) /// /// The name of the property. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + PropertyChanged?.Invoke(this, new(propertyName)); /// /// Releases managed resources used by the component. diff --git a/src/ReactiveUI.Blazor/ReactiveOwningComponentBase.cs b/src/ReactiveUI.Blazor/ReactiveOwningComponentBase.cs index 3233d03188..558c98ae41 100644 --- a/src/ReactiveUI.Blazor/ReactiveOwningComponentBase.cs +++ b/src/ReactiveUI.Blazor/ReactiveOwningComponentBase.cs @@ -1,13 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; - using Microsoft.AspNetCore.Components; - using ReactiveUI.Blazor.Internal; namespace ReactiveUI.Blazor; @@ -30,13 +27,14 @@ namespace ReactiveUI.Blazor; /// managed by the base class. /// /// +[SuppressMessage("Usage", "BL0007:Component parameters should be auto properties", Justification = "Needed for design of the properties")] public class ReactiveOwningComponentBase : OwningComponentBase, IViewFor, INotifyPropertyChanged, ICanActivate where T : class, INotifyPropertyChanged { /// /// Encapsulates reactive state and lifecycle management for this component. /// - private readonly ReactiveComponentState _state = new(); + private readonly ReactiveComponentState _state = new(); /// /// Backing field for . @@ -88,9 +86,16 @@ protected override void OnInitialized() /// #if NET6_0_OR_GREATER - [RequiresUnreferencedCode("OnAfterRender wires reactive subscriptions that may not be trimming-safe in all environments.")] - [SuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "ComponentBase is an external reference")] - [SuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "ComponentBase is an external reference")] + [RequiresUnreferencedCode( + "OnAfterRender wires reactive subscriptions that may not be trimming-safe in all environments.")] + [SuppressMessage( + "AOT", + "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "ComponentBase is an external reference")] + [SuppressMessage( + "Trimming", + "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "ComponentBase is an external reference")] #endif protected override void OnAfterRender(bool firstRender) { @@ -117,7 +122,7 @@ protected override void OnAfterRender(bool firstRender) /// /// The name of the changed property. protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + PropertyChanged?.Invoke(this, new(propertyName)); /// protected override void Dispose(bool disposing) diff --git a/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj b/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj index 82f45d70ff..b8dbf8880a 100644 --- a/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj +++ b/src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj @@ -1,16 +1,15 @@ - + $(ReactiveUIModernTargets) Contains the ReactiveUI platform specific extensions for Blazor mvvm;reactiveui;rx;reactive extensions;observable;LINQ;eventsnet;netstandard;blazor;web; - $(NoWarn);BL0007; - - - + + + - + diff --git a/src/ReactiveUI.Blazor/Registrations.cs b/src/ReactiveUI.Blazor/Registrations.cs index 7dc94ceb87..e47606e27b 100644 --- a/src/ReactiveUI.Blazor/Registrations.cs +++ b/src/ReactiveUI.Blazor/Registrations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -42,9 +42,11 @@ public void Register(IRegistrar registrar) registrar.RegisterConstant(static () => new NullableDecimalToStringTypeConverter()); registrar.RegisterConstant(static () => new PlatformOperations()); - if (Type.GetType("Mono.Runtime") is not null) + if (Type.GetType("Mono.Runtime") is null) { - PlatformEnlightenmentProvider.Current.EnableWasm(); + return; } + + PlatformEnlightenmentProvider.Current.EnableWasm(); } } diff --git a/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs b/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs index 04c8ff0cb4..4b56bd3bd4 100644 --- a/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs +++ b/src/ReactiveUI.Blend/FollowObservableStateBehavior.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -19,14 +19,23 @@ public class FollowObservableStateBehavior : Behavior /// The state observable dependency property. /// public static readonly DependencyProperty StateObservableProperty = - DependencyProperty.Register("StateObservable", typeof(IObservable), typeof(FollowObservableStateBehavior), new PropertyMetadata(null, OnStateObservableChanged)); + DependencyProperty.Register( + "StateObservable", + typeof(IObservable), + typeof(FollowObservableStateBehavior), + new(null, OnStateObservableChanged)); /// /// The target object dependency property. /// public static readonly DependencyProperty TargetObjectProperty = - DependencyProperty.Register("TargetObject", typeof(FrameworkElement), typeof(FollowObservableStateBehavior), new PropertyMetadata(null)); + DependencyProperty.Register( + "TargetObject", + typeof(FrameworkElement), + typeof(FollowObservableStateBehavior), + new(null)); + /// The current subscription watching the state observable. private IDisposable? _watcher; /// @@ -63,7 +72,9 @@ public FrameworkElement TargetObject /// /// The sender. /// The event args. - internal static void InternalOnStateObservableChangedForTesting(DependencyObject? sender, DependencyPropertyChangedEventArgs e) => + internal static void InternalOnStateObservableChangedForTesting( + DependencyObject? sender, + DependencyPropertyChangedEventArgs e) => OnStateObservableChanged(sender, e); /// diff --git a/src/ReactiveUI.Blend/ObservableTrigger.cs b/src/ReactiveUI.Blend/ObservableTrigger.cs index dcf44beb0b..3bdbc756d3 100644 --- a/src/ReactiveUI.Blend/ObservableTrigger.cs +++ b/src/ReactiveUI.Blend/ObservableTrigger.cs @@ -1,11 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Reactive.Concurrency; using System.Windows; - using Microsoft.Xaml.Behaviors; namespace ReactiveUI.Blend; @@ -19,8 +18,13 @@ public class ObservableTrigger : TriggerBase /// The dependency property registration for the Observable property. /// public static readonly DependencyProperty ObservableProperty = - DependencyProperty.Register("Observable", typeof(IObservable), typeof(ObservableTrigger), new PropertyMetadata(OnObservableChanged)); + DependencyProperty.Register( + "Observable", + typeof(IObservable), + typeof(ObservableTrigger), + new(OnObservableChanged)); + /// The current subscription watching the trigger observable. private IDisposable? _watcher; /// @@ -48,7 +52,9 @@ public IObservable Observable /// /// The sender. /// The event args. - internal static void InternalOnObservableChangedForTesting(DependencyObject sender, DependencyPropertyChangedEventArgs e) => + internal static void InternalOnObservableChangedForTesting( + DependencyObject sender, + DependencyPropertyChangedEventArgs e) => OnObservableChanged(sender, e); /// @@ -58,7 +64,7 @@ internal static void InternalOnObservableChangedForTesting(DependencyObject send /// The instance containing the event data. protected static void OnObservableChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { - ArgumentExceptionHelper.ThrowIfNotOfType(sender, nameof(sender)); + ArgumentExceptionHelper.ThrowIfNotOfType(sender); var triggerItem = (ObservableTrigger)sender; if (triggerItem._watcher is not null) @@ -75,15 +81,15 @@ protected static void OnObservableChanged(DependencyObject sender, DependencyPro var newValue = (IObservable)e.NewValue; var scheduler = triggerItem.SchedulerOverride ?? RxSchedulers.MainThreadScheduler; triggerItem._watcher = newValue.ObserveOn(scheduler).Subscribe( - triggerItem.InvokeActions, - _ => - { - if (!triggerItem.AutoResubscribeOnError) - { - return; - } + triggerItem.InvokeActions, + _ => + { + if (!triggerItem.AutoResubscribeOnError) + { + return; + } - OnObservableChanged(triggerItem, e); - }); + OnObservableChanged(triggerItem, e); + }); } } diff --git a/src/ReactiveUI.Blend/Properties/ReactiveUI.Blend_UWP.rd.xml b/src/ReactiveUI.Blend/Properties/ReactiveUI.Blend_UWP.rd.xml index afe25af789..a369045819 100644 --- a/src/ReactiveUI.Blend/Properties/ReactiveUI.Blend_UWP.rd.xml +++ b/src/ReactiveUI.Blend/Properties/ReactiveUI.Blend_UWP.rd.xml @@ -33,7 +33,7 @@ See the LICENSE file in the project root for more information. - + diff --git a/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj b/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj index 95d14116a5..ba65d8c26c 100644 --- a/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj +++ b/src/ReactiveUI.Blend/ReactiveUI.Blend.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIWindowsOnlyTargets) ReactiveUI.Blend @@ -9,26 +9,26 @@ true - - + + - - - + + + - - + + - - + + - + - + diff --git a/src/ReactiveUI.Drawing/Builder/ReactiveUIBuilderDrawingExtensions.cs b/src/ReactiveUI.Drawing/Builder/ReactiveUIBuilderDrawingExtensions.cs index c5204620f8..25e20e96f5 100644 --- a/src/ReactiveUI.Drawing/Builder/ReactiveUIBuilderDrawingExtensions.cs +++ b/src/ReactiveUI.Drawing/Builder/ReactiveUIBuilderDrawingExtensions.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace ReactiveUI.Builder; /// diff --git a/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj b/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj index dc1d059e6b..edded9812c 100644 --- a/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj +++ b/src/ReactiveUI.Drawing/ReactiveUI.Drawing.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIFinalTargetFrameworks) ReactiveUI.Drawing @@ -15,9 +15,9 @@ 6.5 - - - + + + 10.0.19041.57 @@ -26,15 +26,15 @@ - - - + + + - + - - + + - \ No newline at end of file + diff --git a/src/ReactiveUI.Drawing/Registrations.cs b/src/ReactiveUI.Drawing/Registrations.cs index 3c2157c46b..6f5674e206 100644 --- a/src/ReactiveUI.Drawing/Registrations.cs +++ b/src/ReactiveUI.Drawing/Registrations.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - namespace ReactiveUI.Drawing; /// diff --git a/src/ReactiveUI.Maui/ActivationForViewFetcher.cs b/src/ReactiveUI.Maui/ActivationForViewFetcher.cs index fcd3d7638a..007c98bb62 100644 --- a/src/ReactiveUI.Maui/ActivationForViewFetcher.cs +++ b/src/ReactiveUI.Maui/ActivationForViewFetcher.cs @@ -1,18 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Reflection; -#if WINUI_TARGET -using Microsoft.UI.Xaml; - -using ReactiveUI.Maui.Internal; - -using Windows.Foundation; -#endif - #if IS_WINUI namespace ReactiveUI.WinUI; #endif @@ -34,11 +26,12 @@ public int GetAffinityForView(Type view) => typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) #endif #if IS_MAUI - typeof(Page).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) || - typeof(View).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) || - typeof(Cell).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) + typeof(Page).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) || + typeof(View).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) || + typeof(Cell).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) #endif - ? 10 : 0; + ? BindingAffinity.ExactType + : 0; /// public IObservable GetActivationForView(IActivatableView view) @@ -58,10 +51,16 @@ public IObservable GetActivationForView(IActivatableView view) return activation.DistinctUntilChanged(); } + /// Gets the activation stream for an , or null when not applicable. + /// The view to observe, or null. + /// The activation stream, or null when is null. private static IObservable? GetActivationFor(ICanActivate? canActivate) => canActivate?.Activated.Select(static _ => true).Merge(canActivate.Deactivated.Select(static _ => false)); #if IS_MAUI + /// Gets the activation stream for a , or null when not applicable. + /// The page to observe, or null. + /// The activation stream, or null when is null. private static IObservable? GetActivationFor(Page? page) { if (page is null) @@ -70,28 +69,31 @@ public IObservable GetActivationForView(IActivatableView view) } var appearing = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(true); - return Handler; - }, - x => page.Appearing += x, - x => page.Appearing -= x); + eventHandler => + { + void Handler(object? sender, EventArgs e) => eventHandler(true); + return Handler; + }, + x => page.Appearing += x, + x => page.Appearing -= x); var disappearing = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(false); - return Handler; - }, - x => page.Disappearing += x, - x => page.Disappearing -= x); + eventHandler => + { + void Handler(object? sender, EventArgs e) => eventHandler(false); + return Handler; + }, + x => page.Disappearing += x, + x => page.Disappearing -= x); return appearing.Merge(disappearing); } #endif #if IS_MAUI + /// Gets the activation stream for a , or null when not applicable. + /// The view to observe, or null. + /// The activation stream, or null when is null. private static IObservable? GetActivationFor(View? view) { if (view is null) @@ -100,29 +102,32 @@ public IObservable GetActivationForView(IActivatableView view) } var loaded = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(true); - return Handler; - }, - x => view.Loaded += x, - x => view.Loaded -= x); + eventHandler => + { + void Handler(object? sender, EventArgs e) => eventHandler(true); + return Handler; + }, + x => view.Loaded += x, + x => view.Loaded -= x); var unloaded = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(false); - return Handler; - }, - x => view.Unloaded += x, - x => view.Unloaded -= x); + eventHandler => + { + void Handler(object? sender, EventArgs e) => eventHandler(false); + return Handler; + }, + x => view.Unloaded += x, + x => view.Unloaded -= x); return loaded - .Merge(unloaded) - .StartWith(view.IsLoaded) - .DistinctUntilChanged(); + .Merge(unloaded) + .StartWith(view.IsLoaded) + .DistinctUntilChanged(); } + /// Gets the activation stream for a , or null when not applicable. + /// The cell to observe, or null. + /// The activation stream, or null when is null. private static IObservable? GetActivationFor(Cell? cell) { if (cell is null) @@ -131,26 +136,29 @@ public IObservable GetActivationForView(IActivatableView view) } var appearing = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(true); - return Handler; - }, - x => cell.Appearing += x, - x => cell.Appearing -= x); + eventHandler => + { + void Handler(object? sender, EventArgs e) => eventHandler(true); + return Handler; + }, + x => cell.Appearing += x, + x => cell.Appearing -= x); var disappearing = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(false); - return Handler; - }, - x => cell.Disappearing += x, - x => cell.Disappearing -= x); + eventHandler => + { + void Handler(object? sender, EventArgs e) => eventHandler(false); + return Handler; + }, + x => cell.Disappearing += x, + x => cell.Disappearing -= x); return appearing.Merge(disappearing); } #else + /// Gets the activation stream for a , or null when not applicable. + /// The framework element to observe, or null. + /// The activation stream, or null when is null. private static IObservable? GetActivationFor(FrameworkElement? view) { if (view is null) diff --git a/src/ReactiveUI.Maui/AutoSuspendHelper.cs b/src/ReactiveUI.Maui/AutoSuspendHelper.cs index f0f0ef2f08..96a0943d2f 100644 --- a/src/ReactiveUI.Maui/AutoSuspendHelper.cs +++ b/src/ReactiveUI.Maui/AutoSuspendHelper.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -44,18 +44,38 @@ namespace ReactiveUI.Maui; /// /// /// -public partial class AutoSuspendHelper : IEnableLogger, IDisposable +public class AutoSuspendHelper : IEnableLogger, IDisposable { + /// + /// Signals that the application is going to the background. + /// private readonly Subject _onSleep = new(); + + /// + /// Signals that the application is launching fresh. + /// private readonly Subject _onLaunchingNew = new(); + + /// + /// Signals that the application is returning to the foreground. + /// private readonly Subject _onResume = new(); + + /// + /// Signals that the application is starting. + /// private readonly Subject _onStart = new(); - private bool _disposedValue; // To detect redundant calls + + /// + /// To detect redundant calls. + /// + private bool _disposedValue; /// /// Initializes static members of the class. /// - static AutoSuspendHelper() => AppDomain.CurrentDomain.UnhandledException += static (_, _) => UntimelyDemise.OnNext(Unit.Default); + static AutoSuspendHelper() => AppDomain.CurrentDomain.UnhandledException += + static (_, _) => UntimelyDemise.OnNext(Unit.Default); /// /// Initializes a new instance of the class. diff --git a/src/ReactiveUI.Maui/Builder/MauiReactiveUIBuilderExtensions.cs b/src/ReactiveUI.Maui/Builder/MauiReactiveUIBuilderExtensions.cs index 50aae92ebb..23c0b0847a 100644 --- a/src/ReactiveUI.Maui/Builder/MauiReactiveUIBuilderExtensions.cs +++ b/src/ReactiveUI.Maui/Builder/MauiReactiveUIBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,10 +13,14 @@ namespace ReactiveUI.Builder; /// /// MAUI-specific extensions for the ReactiveUI builder. /// -public static partial class MauiReactiveUIBuilderExtensions +public static class MauiReactiveUIBuilderExtensions { #if WINUI_TARGET - private static readonly Lazy LazyWinUIMauiMainThreadScheduler = new(() => new WaitForDispatcherScheduler(static () => DispatcherQueueScheduler.Current)); + /// + /// The lazily-initialized scheduler that marshals work onto the WinUI/MAUI main UI thread. + /// + private static readonly Lazy LazyWinUIMauiMainThreadScheduler = + new(() => new WaitForDispatcherScheduler(static () => DispatcherQueueScheduler.Current)); #endif /// @@ -54,16 +58,24 @@ public static partial class MauiReactiveUIBuilderExtensions /// Use this scheduler to execute actions that must run on the main UI thread of Apple platforms, /// such as updating user interface elements from background operations. This property is available on macOS, iOS, /// and Mac Catalyst platforms. - public static IScheduler AppleMainThreadScheduler { get; } = new WaitForDispatcherScheduler(static () => new NSRunloopScheduler()); + public static IScheduler AppleMainThreadScheduler { get; } = + new WaitForDispatcherScheduler(static () => new NSRunloopScheduler()); #endif + /// + /// Configures ReactiveUI for MAUI platform with appropriate schedulers and platform services. + /// + /// The builder instance. + /// The builder instance for chaining. + public static IReactiveUIBuilder WithMaui(this IReactiveUIBuilder builder) => builder.WithMaui(null); + /// /// Configures ReactiveUI for MAUI platform with appropriate schedulers and platform services. /// /// The builder instance. /// The MAUI dispatcher to use for the main thread scheduler. /// The builder instance for chaining. - public static IReactiveUIBuilder WithMaui(this IReactiveUIBuilder builder, IDispatcher? dispatcher = null) + public static IReactiveUIBuilder WithMaui(this IReactiveUIBuilder builder, IDispatcher? dispatcher) { if (builder is null) { @@ -85,7 +97,9 @@ public static IReactiveUIBuilder WithMaui(this IReactiveUIBuilder builder, IDisp /// The reactive UI builder. /// A The builder instance for chaining. /// builder. - public static MauiAppBuilder UseReactiveUI(this MauiAppBuilder builder, Action withReactiveUIBuilder) + public static MauiAppBuilder UseReactiveUI( + this MauiAppBuilder builder, + Action withReactiveUIBuilder) { if (builder is null) { @@ -116,13 +130,20 @@ public static MauiAppBuilder UseReactiveUI(this MauiAppBuilder builder, IDispatc return builder; } + /// + /// Adds the MAUI scheduler. + /// + /// The builder. + /// The builder instance for chaining. + public static IReactiveUIBuilder WithMauiScheduler(this IReactiveUIBuilder builder) => builder.WithMauiScheduler(null); + /// /// Adds the MAUI scheduler. /// /// The builder. /// Optional dispatcher instance to derive the scheduler from. /// The builder instance for chaining. - public static IReactiveUIBuilder WithMauiScheduler(this IReactiveUIBuilder builder, IDispatcher? dispatcher = null) + public static IReactiveUIBuilder WithMauiScheduler(this IReactiveUIBuilder builder, IDispatcher? dispatcher) { if (builder is null) { @@ -154,6 +175,11 @@ public static IReactiveUIBuilder WithMauiConverters(this IReactiveUIBuilder buil .WithFallbackConverter(new ComponentModelFallbackConverter()); } + /// + /// Resolves the main thread scheduler to use based on the current platform and supplied dispatcher. + /// + /// Optional dispatcher to derive the scheduler from. + /// The resolved main thread scheduler. private static IScheduler ResolveMainThreadScheduler(IDispatcher? dispatcher) { if (dispatcher is not null) @@ -180,18 +206,29 @@ private static IScheduler ResolveMainThreadScheduler(IDispatcher? dispatcher) /// /// Scheduler implementation that marshals work onto a provided MAUI dispatcher. /// - private sealed partial class MauiDispatcherScheduler(IDispatcher dispatcher) : LocalScheduler + private sealed class MauiDispatcherScheduler(IDispatcher dispatcher) : LocalScheduler { + /// + /// The dispatcher used to marshal scheduled work onto the MAUI main thread. + /// private readonly IDispatcher _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); /// /// Gets the current timestamp for the scheduler. /// + [SuppressMessage( + "Major Code Smell", + "S6354:Use a testable (date) time provider", + Justification = "Scheduler intentionally uses real time.")] public override DateTimeOffset Now => DateTimeOffset.Now; /// /// Schedules immediate work on the dispatcher. /// + /// The type of the state passed to the action. + /// The state to pass to the action. + /// The action to execute. + /// A disposable that cancels the scheduled work. public override IDisposable Schedule(TState state, Func action) { if (action is null) @@ -203,10 +240,12 @@ public override IDisposable Schedule(TState state, Func /// Schedules work to execute after the specified delay. /// - public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action) + /// The type of the state passed to the action. + /// The state to pass to the action. + /// The relative delay before the action executes. + /// The action to execute. + /// A disposable that cancels the scheduled work. + public override IDisposable Schedule( + TState state, + TimeSpan dueTime, + Func action) { if (action is null) { @@ -243,15 +290,17 @@ public override IDisposable Schedule(TState state, TimeSpan dueTime, Fun timer.Interval = normalized; EventHandler? handler = null; - handler = (sender, args) => + handler = (_, _) => { timer.Tick -= handler; timer.Stop(); - if (!disposable.IsDisposed) + if (disposable.IsDisposed) { - disposable.Disposable = action(this, state); + return; } + + disposable.Disposable = action(this, state); }; timer.Tick += handler; @@ -267,7 +316,15 @@ public override IDisposable Schedule(TState state, TimeSpan dueTime, Fun /// /// Schedules work to execute at the specified absolute time. /// - public override IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) => + /// The type of the state passed to the action. + /// The state to pass to the action. + /// The absolute time at which the action executes. + /// The action to execute. + /// A disposable that cancels the scheduled work. + public override IDisposable Schedule( + TState state, + DateTimeOffset dueTime, + Func action) => Schedule(state, dueTime - Now, action); } } diff --git a/src/ReactiveUI.Maui/Common/AutoDataTemplateBindingHook.cs b/src/ReactiveUI.Maui/Common/AutoDataTemplateBindingHook.cs index 6027ac8905..822b068d82 100644 --- a/src/ReactiveUI.Maui/Common/AutoDataTemplateBindingHook.cs +++ b/src/ReactiveUI.Maui/Common/AutoDataTemplateBindingHook.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if WINUI_TARGET - -using System.Diagnostics.CodeAnalysis; - using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Markup; @@ -27,14 +24,21 @@ public class AutoDataTemplateBindingHook : IPropertyBindingHook { const string template = "" + - "" + + "" + ""; return (DataTemplate)XamlReader.Load(template); }); /// - public bool ExecuteHook(object? source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction) + public bool ExecuteHook( + object? source, + object target, + Func[]> getCurrentViewModelProperties, + Func[]> getCurrentViewProperties, + BindingDirection direction) { ArgumentNullException.ThrowIfNull(getCurrentViewProperties); diff --git a/src/ReactiveUI.Maui/Common/BooleanToVisibilityHint.cs b/src/ReactiveUI.Maui/Common/BooleanToVisibilityHint.cs index 0dbb15d4a2..c96ddb0120 100644 --- a/src/ReactiveUI.Maui/Common/BooleanToVisibilityHint.cs +++ b/src/ReactiveUI.Maui/Common/BooleanToVisibilityHint.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -9,6 +9,10 @@ namespace ReactiveUI; /// Enum that hints at the visibility of a ui element. /// [Flags] +[SuppressMessage( + "Minor Code Smell", + "S2342:Enumeration types should comply with a naming convention", + Justification = "Established public API; renaming is breaking.")] public enum BooleanToVisibilityHint { /// @@ -24,5 +28,5 @@ public enum BooleanToVisibilityHint /// /// Use the hidden version rather than the Collapsed. /// - UseHidden = 1 << 2, + UseHidden = 1 << 2 } diff --git a/src/ReactiveUI.Maui/Common/BooleanToVisibilityTypeConverter.cs b/src/ReactiveUI.Maui/Common/BooleanToVisibilityTypeConverter.cs index 48ad0569d8..6771dcf229 100644 --- a/src/ReactiveUI.Maui/Common/BooleanToVisibilityTypeConverter.cs +++ b/src/ReactiveUI.Maui/Common/BooleanToVisibilityTypeConverter.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - #if WINUI_TARGET using Microsoft.UI.Xaml; #else @@ -36,10 +34,10 @@ namespace ReactiveUI; public sealed class BooleanToVisibilityTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(bool from, object? conversionHint, [NotNullWhen(true)] out Visibility result) + public override bool TryConvert(bool from, object? conversionHint, out Visibility result) { var hint = conversionHint is BooleanToVisibilityHint visibilityHint ? visibilityHint diff --git a/src/ReactiveUI.Maui/Common/PlatformOperations.cs b/src/ReactiveUI.Maui/Common/PlatformOperations.cs index e12b4c1545..32f856c239 100644 --- a/src/ReactiveUI.Maui/Common/PlatformOperations.cs +++ b/src/ReactiveUI.Maui/Common/PlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Maui/Common/ReactivePage.cs b/src/ReactiveUI.Maui/Common/ReactivePage.cs index eb492ddc6d..013a063471 100644 --- a/src/ReactiveUI.Maui/Common/ReactivePage.cs +++ b/src/ReactiveUI.Maui/Common/ReactivePage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -79,10 +79,16 @@ namespace ReactiveUI; /// /// The type of the view model backing the view. /// -[SuppressMessage("WinRT", "CsWinRT1029:Types used in signatures should be WinRT types", Justification = "This is a netstandard2.0 library")] -public partial class ReactivePage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : - Page, IViewFor - where TViewModel : class +[SuppressMessage( + "WinRT", + "CsWinRT1029:Types used in signatures should be WinRT types", + Justification = "This is a netstandard2.0 library")] +[SuppressMessage("Roslynator", "RCS1043:Remove \'partial\' modifier from type with a single part", Justification = "This is a netstandard2.0 library")] +[SuppressMessage("Minor Code Smell", "S2333:Redundant modifiers should not be used", Justification = "This is a netstandard2.0 library")] +public partial class ReactivePage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : + Page, IViewFor + where TViewModel : class { #if WINUI_TARGET /// @@ -99,12 +105,10 @@ public partial class ReactivePage<[DynamicallyAccessedMembers(DynamicallyAccesse /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactivePage), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactivePage), + propertyChanged: OnViewModelChanged); #endif /// @@ -134,6 +138,13 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; #endif } diff --git a/src/ReactiveUI.Maui/Common/ReactiveUserControl.cs b/src/ReactiveUI.Maui/Common/ReactiveUserControl.cs index 2ffdd8965f..ec2083a060 100644 --- a/src/ReactiveUI.Maui/Common/ReactiveUserControl.cs +++ b/src/ReactiveUI.Maui/Common/ReactiveUserControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Maui/Common/RoutedViewHost.cs b/src/ReactiveUI.Maui/Common/RoutedViewHost.cs index 2d6588ba4c..e6c9dd4dfb 100644 --- a/src/ReactiveUI.Maui/Common/RoutedViewHost.cs +++ b/src/ReactiveUI.Maui/Common/RoutedViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs b/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs index 0286a7ae0e..421ba2ffcb 100644 --- a/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs +++ b/src/ReactiveUI.Maui/Common/ViewModelViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Maui/Common/VisibilityToBooleanTypeConverter.cs b/src/ReactiveUI.Maui/Common/VisibilityToBooleanTypeConverter.cs index c415698f2e..e1a1ed0bc1 100644 --- a/src/ReactiveUI.Maui/Common/VisibilityToBooleanTypeConverter.cs +++ b/src/ReactiveUI.Maui/Common/VisibilityToBooleanTypeConverter.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - #if WINUI_TARGET using Microsoft.UI.Xaml; #else @@ -35,10 +33,10 @@ namespace ReactiveUI; public sealed class VisibilityToBooleanTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(Visibility from, object? conversionHint, [NotNullWhen(true)] out bool result) + public override bool TryConvert(Visibility from, object? conversionHint, out bool result) { var hint = conversionHint is BooleanToVisibilityHint visibilityHint ? visibilityHint diff --git a/src/ReactiveUI.Maui/DisableAnimationAttribute.cs b/src/ReactiveUI.Maui/DisableAnimationAttribute.cs index da1c8608eb..0de86c7803 100644 --- a/src/ReactiveUI.Maui/DisableAnimationAttribute.cs +++ b/src/ReactiveUI.Maui/DisableAnimationAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Maui/Internal/MauiReactiveHelpers.cs b/src/ReactiveUI.Maui/Internal/MauiReactiveHelpers.cs index b56b85886d..54f82e6ab0 100644 --- a/src/ReactiveUI.Maui/Internal/MauiReactiveHelpers.cs +++ b/src/ReactiveUI.Maui/Internal/MauiReactiveHelpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -38,11 +38,13 @@ public static IObservable CreatePropertyChangedPulse(INotifyPropertyChange { void Handler(object? sender, PropertyChangedEventArgs e) { - if (string.IsNullOrEmpty(e.PropertyName) || - string.Equals(e.PropertyName, propertyName, StringComparison.Ordinal)) + if (!string.IsNullOrEmpty(e.PropertyName) && + !string.Equals(e.PropertyName, propertyName, StringComparison.Ordinal)) { - observer.OnNext(Unit.Default); + return; } + + observer.OnNext(Unit.Default); } source.PropertyChanged += Handler; @@ -80,11 +82,13 @@ public static IObservable CreatePropertyValueObservable( void Handler(object? sender, PropertyChangedEventArgs e) { - if (string.IsNullOrEmpty(e.PropertyName) || - string.Equals(e.PropertyName, propertyName, StringComparison.Ordinal)) + if (!string.IsNullOrEmpty(e.PropertyName) && + !string.Equals(e.PropertyName, propertyName, StringComparison.Ordinal)) { - observer.OnNext(getPropertyValue()); + return; } + + observer.OnNext(getPropertyValue()); } source.PropertyChanged += Handler; diff --git a/src/ReactiveUI.Maui/ReactiveCarouselView.cs b/src/ReactiveUI.Maui/ReactiveCarouselView.cs index eca2058766..71672d23b9 100644 --- a/src/ReactiveUI.Maui/ReactiveCarouselView.cs +++ b/src/ReactiveUI.Maui/ReactiveCarouselView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,19 +13,20 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveCarouselView<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : CarouselView, IViewFor +public class ReactiveCarouselView< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : CarouselView, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveCarouselView), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveCarouselView), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -50,5 +51,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveContentPage.cs b/src/ReactiveUI.Maui/ReactiveContentPage.cs index 9091f9ee60..6e9554069a 100644 --- a/src/ReactiveUI.Maui/ReactiveContentPage.cs +++ b/src/ReactiveUI.Maui/ReactiveContentPage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,19 +13,20 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveContentPage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ContentPage, IViewFor +public class ReactiveContentPage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : ContentPage, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveContentPage), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveContentPage), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -50,5 +51,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveContentView.cs b/src/ReactiveUI.Maui/ReactiveContentView.cs index dabf645a0d..bf0ee5ae6e 100644 --- a/src/ReactiveUI.Maui/ReactiveContentView.cs +++ b/src/ReactiveUI.Maui/ReactiveContentView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,19 +13,20 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveContentView<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ContentView, IViewFor +public class ReactiveContentView< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : ContentView, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveContentView), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveContentView), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -50,5 +51,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveEntryCell.cs b/src/ReactiveUI.Maui/ReactiveEntryCell.cs index d1566b6e17..3662ce28b6 100644 --- a/src/ReactiveUI.Maui/ReactiveEntryCell.cs +++ b/src/ReactiveUI.Maui/ReactiveEntryCell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,21 +14,26 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -[Obsolete("ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[Obsolete( + "ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[SuppressMessage( + "Info Code Smell", + "S1133:Deprecated code should be removed", + Justification = "Intentional public API deprecation retained for backwards compatibility.")] [ExcludeFromCodeCoverage] -public partial class ReactiveEntryCell<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : EntryCell, IViewFor +public class ReactiveEntryCell< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : EntryCell, + IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveEntryCell), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveEntryCell), + propertyChanged: OnViewModelChanged); /// public TViewModel? ViewModel @@ -51,5 +56,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveFlyoutPage.cs b/src/ReactiveUI.Maui/ReactiveFlyoutPage.cs index 191ea310dd..1e65749f85 100644 --- a/src/ReactiveUI.Maui/ReactiveFlyoutPage.cs +++ b/src/ReactiveUI.Maui/ReactiveFlyoutPage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,19 +13,20 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveFlyoutPage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : FlyoutPage, IViewFor +public class ReactiveFlyoutPage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : FlyoutPage, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveFlyoutPage), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveFlyoutPage), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -50,5 +51,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveImageCell.cs b/src/ReactiveUI.Maui/ReactiveImageCell.cs index d89b8452b4..2d5537b16d 100644 --- a/src/ReactiveUI.Maui/ReactiveImageCell.cs +++ b/src/ReactiveUI.Maui/ReactiveImageCell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,21 +14,26 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -[Obsolete("ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[Obsolete( + "ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[SuppressMessage( + "Info Code Smell", + "S1133:Deprecated code should be removed", + Justification = "Intentional public API deprecation retained for backwards compatibility.")] [ExcludeFromCodeCoverage] -public partial class ReactiveImageCell<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ImageCell, IViewFor +public class ReactiveImageCell< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ImageCell, + IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveImageCell), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveImageCell), + propertyChanged: OnViewModelChanged); /// public TViewModel? ViewModel @@ -51,5 +56,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveImageItemView.cs b/src/ReactiveUI.Maui/ReactiveImageItemView.cs index 832868b266..b84e870a54 100644 --- a/src/ReactiveUI.Maui/ReactiveImageItemView.cs +++ b/src/ReactiveUI.Maui/ReactiveImageItemView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,7 +15,10 @@ namespace ReactiveUI.Maui; /// /// The type of the view model. /// -public partial class ReactiveImageItemView<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ReactiveContentView +public class ReactiveImageItemView< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : ReactiveContentView where TViewModel : class { /// @@ -25,7 +28,7 @@ public partial class ReactiveImageItemView<[DynamicallyAccessedMembers(Dynamical nameof(ImageSource), typeof(ImageSource), typeof(ReactiveImageItemView), - default(ImageSource)); + propertyChanged: OnImageSourceChanged); /// /// The text bindable property for the primary text. @@ -34,7 +37,7 @@ public partial class ReactiveImageItemView<[DynamicallyAccessedMembers(Dynamical nameof(Text), typeof(string), typeof(ReactiveImageItemView), - default(string)); + propertyChanged: OnTextChanged); /// /// The detail bindable property for the secondary text. @@ -43,7 +46,7 @@ public partial class ReactiveImageItemView<[DynamicallyAccessedMembers(Dynamical nameof(Detail), typeof(string), typeof(ReactiveImageItemView), - default(string)); + propertyChanged: OnDetailChanged); /// /// The text color bindable property. @@ -52,7 +55,7 @@ public partial class ReactiveImageItemView<[DynamicallyAccessedMembers(Dynamical nameof(TextColor), typeof(Color), typeof(ReactiveImageItemView), - default(Color)); + propertyChanged: OnTextColorChanged); /// /// The detail color bindable property. @@ -61,11 +64,51 @@ public partial class ReactiveImageItemView<[DynamicallyAccessedMembers(Dynamical nameof(DetailColor), typeof(Color), typeof(ReactiveImageItemView), - default(Color)); + propertyChanged: OnDetailColorChanged); - private readonly CompositeDisposable _propertyBindings = []; + /// + /// The width and height of the image, in device-independent units. + /// + private const double ImageSize = 40; + + /// + /// The font size of the primary text label. + /// + private const double PrimaryFontSize = 16; + + /// + /// The font size of the detail text label. + /// + private const double DetailFontSize = 12; + + /// + /// The opacity applied to the detail text label. + /// + private const double DetailOpacity = 0.7; + + /// + /// The padding around the content layout. + /// + private const double ContentPadding = 16; + + /// + /// The spacing between the image and the text layout. + /// + private const double ContentSpacing = 12; + + /// + /// The image element that displays the bound image source. + /// private readonly Image _image; + + /// + /// The label that displays the primary text. + /// private readonly Label _textLabel; + + /// + /// The label that displays the detail text. + /// private readonly Label _detailLabel; /// @@ -73,28 +116,23 @@ public partial class ReactiveImageItemView<[DynamicallyAccessedMembers(Dynamical /// public ReactiveImageItemView() { - _image = new Image + _image = new() { - WidthRequest = 40, - HeightRequest = 40, + WidthRequest = ImageSize, + HeightRequest = ImageSize, VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Start, Source = ImageSource // Set initial value }; - _textLabel = new Label + _textLabel = new() { - FontSize = 16, - VerticalOptions = LayoutOptions.Center, - Text = Text // Set initial value + FontSize = PrimaryFontSize, VerticalOptions = LayoutOptions.Center, Text = Text // Set initial value }; - _detailLabel = new Label + _detailLabel = new() { - FontSize = 12, - VerticalOptions = LayoutOptions.Center, - Opacity = 0.7, - Text = Detail // Set initial value + FontSize = DetailFontSize, VerticalOptions = LayoutOptions.Center, Opacity = DetailOpacity, Text = Detail // Set initial value }; var textStackLayout = new StackLayout @@ -105,37 +143,14 @@ public ReactiveImageItemView() Children = { _textLabel, _detailLabel } }; - var mainStackLayout = new StackLayout + Content = new StackLayout { Orientation = StackOrientation.Horizontal, VerticalOptions = LayoutOptions.Center, - Padding = 16, - Spacing = 12, + Padding = ContentPadding, + Spacing = ContentSpacing, Children = { _image, textStackLayout } }; - - Content = mainStackLayout; - - // Use expression-based property observation instead of string-based bindings (AOT-safe) - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(ImageSource), () => ImageSource) - .Subscribe(value => _image.Source = value) - .DisposeWith(_propertyBindings); - - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(Text), () => Text) - .Subscribe(value => _textLabel.Text = value) - .DisposeWith(_propertyBindings); - - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(TextColor), () => TextColor) - .Subscribe(value => _textLabel.TextColor = value) - .DisposeWith(_propertyBindings); - - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(Detail), () => Detail) - .Subscribe(value => _detailLabel.Text = value) - .DisposeWith(_propertyBindings); - - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(DetailColor), () => DetailColor) - .Subscribe(value => _detailLabel.TextColor = value) - .DisposeWith(_propertyBindings); } /// @@ -182,4 +197,84 @@ public Color DetailColor get => (Color)GetValue(DetailColorProperty); set => SetValue(DetailColorProperty, value); } + + /// + /// Handles changes to the property by updating the image control. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnImageSourceChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveImageItemView view) + { + return; + } + + view._image.Source = (ImageSource?)newValue; + } + + /// + /// Handles changes to the property by updating the primary text label. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnTextChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveImageItemView view) + { + return; + } + + view._textLabel.Text = (string?)newValue; + } + + /// + /// Handles changes to the property by updating the primary text label color. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnTextColorChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveImageItemView view) + { + return; + } + + view._textLabel.TextColor = (Color?)newValue; + } + + /// + /// Handles changes to the property by updating the detail text label. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnDetailChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveImageItemView view) + { + return; + } + + view._detailLabel.Text = (string?)newValue; + } + + /// + /// Handles changes to the property by updating the detail text label color. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnDetailColorChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveImageItemView view) + { + return; + } + + view._detailLabel.TextColor = (Color?)newValue; + } } diff --git a/src/ReactiveUI.Maui/ReactiveMasterDetailPage.cs b/src/ReactiveUI.Maui/ReactiveMasterDetailPage.cs index a7c9b00cf2..402801af6a 100644 --- a/src/ReactiveUI.Maui/ReactiveMasterDetailPage.cs +++ b/src/ReactiveUI.Maui/ReactiveMasterDetailPage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,19 +13,20 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveMasterDetailPage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : FlyoutPage, IViewFor +public class ReactiveMasterDetailPage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : FlyoutPage, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveMasterDetailPage), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveMasterDetailPage), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -50,5 +51,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveMultiPage.cs b/src/ReactiveUI.Maui/ReactiveMultiPage.cs index 41e3a97670..5b50fa569d 100644 --- a/src/ReactiveUI.Maui/ReactiveMultiPage.cs +++ b/src/ReactiveUI.Maui/ReactiveMultiPage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,7 +14,13 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public abstract class ReactiveMultiPage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties)] TPage, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : MultiPage, IViewFor +public abstract class ReactiveMultiPage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicProperties)] + TPage, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : + MultiPage, IViewFor where TPage : Page where TViewModel : class { @@ -22,12 +28,10 @@ public abstract class ReactiveMultiPage<[DynamicallyAccessedMembers(DynamicallyA /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveMultiPage), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveMultiPage), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -52,5 +56,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveNavigationPage.cs b/src/ReactiveUI.Maui/ReactiveNavigationPage.cs index e1701654bc..d849e2673c 100644 --- a/src/ReactiveUI.Maui/ReactiveNavigationPage.cs +++ b/src/ReactiveUI.Maui/ReactiveNavigationPage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -12,19 +12,20 @@ namespace ReactiveUI.Maui; /// /// The type of the view model. /// -public partial class ReactiveNavigationPage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : NavigationPage, IViewFor +public class ReactiveNavigationPage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : NavigationPage, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveNavigationPage), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveNavigationPage), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -49,5 +50,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveShell.cs b/src/ReactiveUI.Maui/ReactiveShell.cs index d28a9d52f7..e62714f69f 100644 --- a/src/ReactiveUI.Maui/ReactiveShell.cs +++ b/src/ReactiveUI.Maui/ReactiveShell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,19 +13,19 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveShell<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : Shell, IViewFor +public class ReactiveShell< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : Shell, + IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveShell), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveShell), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -50,5 +50,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveShellContent.cs b/src/ReactiveUI.Maui/ReactiveShellContent.cs index c3213d8ec9..5a064fcfca 100644 --- a/src/ReactiveUI.Maui/ReactiveShellContent.cs +++ b/src/ReactiveUI.Maui/ReactiveShellContent.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,30 +13,31 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveShellContent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ShellContent, IActivatableView +public class ReactiveShellContent< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : ShellContent, IActivatableView where TViewModel : class { /// /// The contract property. /// public static readonly BindableProperty ContractProperty = BindableProperty.Create( - nameof(Contract), - typeof(string), - typeof(ReactiveShellContent), - null, - BindingMode.Default, - propertyChanged: ViewModelChanged); + nameof(Contract), + typeof(string), + typeof(ReactiveShellContent), + defaultBindingMode: BindingMode.Default, + propertyChanged: ViewModelChanged); /// /// The view model property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveShellContent), - default(TViewModel), - BindingMode.Default, - propertyChanged: ViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveShellContent), + defaultBindingMode: BindingMode.Default, + propertyChanged: ViewModelChanged); /// /// Initializes a new instance of the class. @@ -44,10 +45,12 @@ public partial class ReactiveShellContent<[DynamicallyAccessedMembers(Dynamicall public ReactiveShellContent() { var view = AppLocator.Current.GetService>(Contract); - if (view is not null) + if (view is null) { - ContentTemplate = new DataTemplate(() => view); + return; } + + ContentTemplate = new(() => view); } /// @@ -74,20 +77,31 @@ public string? Contract set => SetValue(ContractProperty, value); } + /// + /// Handles changes to the or properties by resolving and + /// assigning the associated view as the content template. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. private static void ViewModelChanged(BindableObject bindable, object oldValue, object newValue) { if (AppLocator.Current is null) { - throw new NullReferenceException(nameof(AppLocator.Current)); + throw new InvalidOperationException(nameof(AppLocator.Current)); } - if (bindable is ReactiveShellContent svm) + if (bindable is not ReactiveShellContent svm) { - var view = AppLocator.Current.GetService>(svm.Contract); - if (view is not null) - { - svm.ContentTemplate = new DataTemplate(() => view); - } + return; } + + var view = AppLocator.Current.GetService>(svm.Contract); + if (view is null) + { + return; + } + + svm.ContentTemplate = new(() => view); } } diff --git a/src/ReactiveUI.Maui/ReactiveSwitchCell.cs b/src/ReactiveUI.Maui/ReactiveSwitchCell.cs index 1a6dfc8979..fac0ac483e 100644 --- a/src/ReactiveUI.Maui/ReactiveSwitchCell.cs +++ b/src/ReactiveUI.Maui/ReactiveSwitchCell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,21 +14,27 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -[Obsolete("ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[Obsolete( + "ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[SuppressMessage( + "Info Code Smell", + "S1133:Deprecated code should be removed", + Justification = "Intentional public API deprecation retained for backwards compatibility.")] [ExcludeFromCodeCoverage] -public partial class ReactiveSwitchCell<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : SwitchCell, IViewFor +public class ReactiveSwitchCell< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : SwitchCell, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveSwitchCell), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveSwitchCell), + propertyChanged: OnViewModelChanged); /// public TViewModel? ViewModel @@ -51,5 +57,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveTabbedPage.cs b/src/ReactiveUI.Maui/ReactiveTabbedPage.cs index 9c2e555845..098a0b0689 100644 --- a/src/ReactiveUI.Maui/ReactiveTabbedPage.cs +++ b/src/ReactiveUI.Maui/ReactiveTabbedPage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,19 +13,20 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -public partial class ReactiveTabbedPage<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : TabbedPage, IViewFor +public class ReactiveTabbedPage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : TabbedPage, IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveTabbedPage), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveTabbedPage), + propertyChanged: OnViewModelChanged); /// /// Gets or sets the ViewModel to display. @@ -50,5 +51,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveTextCell.cs b/src/ReactiveUI.Maui/ReactiveTextCell.cs index ad6f945d37..6b562d8065 100644 --- a/src/ReactiveUI.Maui/ReactiveTextCell.cs +++ b/src/ReactiveUI.Maui/ReactiveTextCell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,21 +14,26 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -[Obsolete("ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[Obsolete( + "ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[SuppressMessage( + "Info Code Smell", + "S1133:Deprecated code should be removed", + Justification = "Intentional public API deprecation retained for backwards compatibility.")] [ExcludeFromCodeCoverage] -public partial class ReactiveTextCell<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : TextCell, IViewFor +public class ReactiveTextCell< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : TextCell, + IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveTextCell), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveTextCell), + propertyChanged: OnViewModelChanged); /// public TViewModel? ViewModel @@ -51,5 +56,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/ReactiveTextItemView.cs b/src/ReactiveUI.Maui/ReactiveTextItemView.cs index c23ee8dd32..423682b34e 100644 --- a/src/ReactiveUI.Maui/ReactiveTextItemView.cs +++ b/src/ReactiveUI.Maui/ReactiveTextItemView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,7 +15,10 @@ namespace ReactiveUI.Maui; /// /// The type of the view model. /// -public partial class ReactiveTextItemView<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ReactiveContentView +public class ReactiveTextItemView< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : ReactiveContentView where TViewModel : class { /// @@ -25,7 +28,7 @@ public partial class ReactiveTextItemView<[DynamicallyAccessedMembers(Dynamicall nameof(Text), typeof(string), typeof(ReactiveTextItemView), - default(string)); + propertyChanged: OnTextChanged); /// /// The detail bindable property for the secondary text. @@ -34,7 +37,7 @@ public partial class ReactiveTextItemView<[DynamicallyAccessedMembers(Dynamicall nameof(Detail), typeof(string), typeof(ReactiveTextItemView), - default(string)); + propertyChanged: OnDetailChanged); /// /// The text color bindable property. @@ -43,7 +46,7 @@ public partial class ReactiveTextItemView<[DynamicallyAccessedMembers(Dynamicall nameof(TextColor), typeof(Color), typeof(ReactiveTextItemView), - default(Color)); + propertyChanged: OnTextColorChanged); /// /// The detail color bindable property. @@ -52,10 +55,36 @@ public partial class ReactiveTextItemView<[DynamicallyAccessedMembers(Dynamicall nameof(DetailColor), typeof(Color), typeof(ReactiveTextItemView), - default(Color)); + propertyChanged: OnDetailColorChanged); - private readonly CompositeDisposable _propertyBindings = []; + /// + /// The font size of the primary text label. + /// + private const double PrimaryFontSize = 16; + + /// + /// The font size of the detail text label. + /// + private const double DetailFontSize = 12; + + /// + /// The opacity applied to the detail text label. + /// + private const double DetailOpacity = 0.7; + + /// + /// The padding around the content layout. + /// + private const double ContentPadding = 16; + + /// + /// The label that displays the primary text. + /// private readonly Label _textLabel; + + /// + /// The label that displays the detail text. + /// private readonly Label _detailLabel; /// @@ -63,47 +92,23 @@ public partial class ReactiveTextItemView<[DynamicallyAccessedMembers(Dynamicall /// public ReactiveTextItemView() { - _textLabel = new Label + _textLabel = new() { - FontSize = 16, - VerticalOptions = LayoutOptions.Center, - Text = Text // Set initial value + FontSize = PrimaryFontSize, VerticalOptions = LayoutOptions.Center, Text = Text // Set initial value }; - _detailLabel = new Label + _detailLabel = new() { - FontSize = 12, - VerticalOptions = LayoutOptions.Center, - Opacity = 0.7, - Text = Detail // Set initial value + FontSize = DetailFontSize, VerticalOptions = LayoutOptions.Center, Opacity = DetailOpacity, Text = Detail // Set initial value }; - var stackLayout = new StackLayout + Content = new StackLayout { Orientation = StackOrientation.Vertical, VerticalOptions = LayoutOptions.Center, - Padding = 16, + Padding = ContentPadding, Children = { _textLabel, _detailLabel } }; - - Content = stackLayout; - - // Use expression-based property observation instead of string-based bindings (AOT-safe) - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(Text), () => Text) - .Subscribe(value => _textLabel.Text = value) - .DisposeWith(_propertyBindings); - - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(TextColor), () => TextColor) - .Subscribe(value => _textLabel.TextColor = value) - .DisposeWith(_propertyBindings); - - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(Detail), () => Detail) - .Subscribe(value => _detailLabel.Text = value) - .DisposeWith(_propertyBindings); - - MauiReactiveHelpers.CreatePropertyValueObservable(this, nameof(DetailColor), () => DetailColor) - .Subscribe(value => _detailLabel.TextColor = value) - .DisposeWith(_propertyBindings); } /// @@ -141,4 +146,68 @@ public Color DetailColor get => (Color)GetValue(DetailColorProperty); set => SetValue(DetailColorProperty, value); } + + /// + /// Handles changes to the property by updating the primary text label. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnTextChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveTextItemView view) + { + return; + } + + view._textLabel.Text = (string?)newValue; + } + + /// + /// Handles changes to the property by updating the detail text label. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnDetailChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveTextItemView view) + { + return; + } + + view._detailLabel.Text = (string?)newValue; + } + + /// + /// Handles changes to the property by updating the primary text label color. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnTextColorChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveTextItemView view) + { + return; + } + + view._textLabel.TextColor = (Color?)newValue; + } + + /// + /// Handles changes to the property by updating the detail text label color. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnDetailColorChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ReactiveTextItemView view) + { + return; + } + + view._detailLabel.TextColor = (Color?)newValue; + } } diff --git a/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj b/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj index 3886fc821a..d2dffb18bf 100644 --- a/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj +++ b/src/ReactiveUI.Maui/ReactiveUI.Maui.csproj @@ -1,4 +1,4 @@ - + $(ReactiveMauiTargets) Contains the ReactiveUI platform specific extensions for Microsoft Maui @@ -21,43 +21,43 @@ $(DefineConstants);WINUI_TARGET - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - + - + - - - + + + - - - - - + + + + + diff --git a/src/ReactiveUI.Maui/ReactiveViewCell.cs b/src/ReactiveUI.Maui/ReactiveViewCell.cs index c21df79c40..5364d1927c 100644 --- a/src/ReactiveUI.Maui/ReactiveViewCell.cs +++ b/src/ReactiveUI.Maui/ReactiveViewCell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,21 +14,26 @@ namespace ReactiveUI.Maui; /// The type of the view model. /// /// -[Obsolete("ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[Obsolete( + "ListView and its cells are obsolete in .NET MAUI, please use CollectionView with a DataTemplate and a ReactiveContentView-based view instead. This will be removed in a future release.")] +[SuppressMessage( + "Info Code Smell", + "S1133:Deprecated code should be removed", + Justification = "Intentional public API deprecation retained for backwards compatibility.")] [ExcludeFromCodeCoverage] -public partial class ReactiveViewCell<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ViewCell, IViewFor +public class ReactiveViewCell< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ViewCell, + IViewFor where TViewModel : class { /// /// The view model bindable property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ReactiveViewCell), - default(TViewModel), - BindingMode.OneWay, - propertyChanged: OnViewModelChanged); + nameof(ViewModel), + typeof(TViewModel), + typeof(ReactiveViewCell), + propertyChanged: OnViewModelChanged); /// public TViewModel? ViewModel @@ -51,5 +56,12 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + /// + /// Updates the binding context when the view model changes. + /// + /// The bindable object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => + bindableObject.BindingContext = newValue; } diff --git a/src/ReactiveUI.Maui/Registrations.cs b/src/ReactiveUI.Maui/Registrations.cs index 3267b0a66f..ccf5561280 100644 --- a/src/ReactiveUI.Maui/Registrations.cs +++ b/src/ReactiveUI.Maui/Registrations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Maui/RoutedViewHost.cs b/src/ReactiveUI.Maui/RoutedViewHost.cs index 2a099b595e..996e12c4ea 100644 --- a/src/ReactiveUI.Maui/RoutedViewHost.cs +++ b/src/ReactiveUI.Maui/RoutedViewHost.cs @@ -1,11 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Specialized; using System.Reflection; - using Microsoft.Maui.Controls; namespace ReactiveUI.Maui; @@ -15,146 +14,55 @@ namespace ReactiveUI.Maui; /// /// /// -public partial class RoutedViewHost : NavigationPage, IActivatableView, IEnableLogger +public class RoutedViewHost : NavigationPage, IActivatableView, IEnableLogger { /// /// The router bindable property. /// public static readonly BindableProperty RouterProperty = BindableProperty.Create( - nameof(Router), - typeof(RoutingState), - typeof(RoutedViewHost), - default(RoutingState)); + nameof(Router), + typeof(RoutingState), + typeof(RoutedViewHost)); /// /// The Set Title on Navigate property. /// public static readonly BindableProperty SetTitleOnNavigateProperty = BindableProperty.Create( - nameof(SetTitleOnNavigate), - typeof(bool), - typeof(RoutedViewHost), - false); + nameof(SetTitleOnNavigate), + typeof(bool), + typeof(RoutedViewHost), + false); + /// + /// The subscriptions created by this host. + /// private readonly CompositeDisposable _subscriptions = []; + + /// + /// The name of the last navigation action that occurred. + /// private string? _action; + + /// + /// A value indicating whether a navigation operation is currently in progress. + /// private bool _currentlyNavigating; /// /// Initializes a new instance of the class. /// - /// You *must* register an IScreen class representing your App's main Screen. - [RequiresUnreferencedCode("This class uses reflection to determine view model types at runtime through ViewLocator, which may be incompatible with trimming.")] + /// You *must* register an IScreen class representing your App's main Screen. + [RequiresUnreferencedCode( + "This class uses reflection to determine view model types at runtime through ViewLocator, which may be incompatible with trimming.")] [RequiresDynamicCode("ViewLocator.ResolveView uses reflection which is incompatible with AOT compilation.")] public RoutedViewHost() { // Subscribe directly without WhenActivated - Observable.FromEventPattern( - x => Router!.NavigationStack.CollectionChanged += x, - x => Router!.NavigationStack.CollectionChanged -= x) - .Where(_ => !_currentlyNavigating && Router?.NavigationStack.Count == 0) - .Subscribe(async _ => await SyncNavigationStacksAsync()) - .DisposeWith(_subscriptions); - - Router? - .NavigateBack - .Subscribe(async _ => - { - try - { - _currentlyNavigating = true; - await PopAsync(); - } - finally - { - _currentlyNavigating = false; - } - - _action = "NavigatedBack"; - InvalidateCurrentViewModel(); - await SyncNavigationStacksAsync(); - }) - .DisposeWith(_subscriptions); - - Router? - .Navigate - .Where(_ => StacksAreDifferent()) - .ObserveOn(RxSchedulers.MainThreadScheduler) - .SelectMany(_ => PagesForViewModel(Router.GetCurrentViewModel())) - .SelectMany(async page => - { - var animated = true; - var attribute = page.GetType().GetCustomAttribute(); - if (attribute is not null) - { - animated = false; - } - - try - { - _currentlyNavigating = true; - await PushAsync(page, animated); - } - finally - { - _currentlyNavigating = false; - } - - await SyncNavigationStacksAsync(); - - return page; - }) - .Subscribe() - .DisposeWith(_subscriptions); - - var poppingEvent = Observable.FromEvent, Unit>( - eventHandler => - { - void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); - return Handler; - }, - x => Popped += x, - x => Popped -= x); - - // NB: User pressed the Application back as opposed to requesting Back via Router.NavigateBack. - poppingEvent - .Where(_ => !_currentlyNavigating && Router is not null) - .Subscribe(_ => - { - if (Router?.NavigationStack.Count > 0) - { - Router.NavigationStack.RemoveAt(Router.NavigationStack.Count - 1); - } - - _action = "Popped"; - InvalidateCurrentViewModel(); - }) - .DisposeWith(_subscriptions); - - var poppingToRootEvent = Observable.FromEvent, Unit>( - eventHandler => - { - void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); - return Handler; - }, - x => PoppedToRoot += x, - x => PoppedToRoot -= x); - - poppingToRootEvent - .Where(_ => !_currentlyNavigating && Router is not null) - .Subscribe(_ => - { - for (var i = Router?.NavigationStack.Count - 1; i > 0; i--) - { - if (i.HasValue) - { - Router?.NavigationStack.RemoveAt(i.Value); - } - } - - _action = "PoppedToRoot"; - InvalidateCurrentViewModel(); - }) - .DisposeWith(_subscriptions); + SubscribeToNavigationStackChanges(); + SubscribeToNavigateBack(); + SubscribeToNavigate(); + SubscribeToPopped(); + SubscribeToPoppedToRoot(); // Perform initial sync asynchronously _ = Task.Run(async () => @@ -169,7 +77,8 @@ public RoutedViewHost() } }); - var screen = AppLocator.Current.GetService() ?? throw new Exception("You *must* register an IScreen class representing your App's main Screen"); + var screen = AppLocator.Current.GetService() ?? + throw new InvalidOperationException("You *must* register an IScreen class representing your App's main Screen"); Router = screen.Router; } @@ -196,8 +105,11 @@ public bool SetTitleOnNavigate /// /// The vm. /// An observable of the page associated to a . - [RequiresUnreferencedCode("This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] - [RequiresDynamicCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), " + + "trimming can't validate that the requirements of those annotations are met.")] protected virtual IObservable PagesForViewModel(IRoutableViewModel? vm) { if (vm is null) @@ -208,9 +120,10 @@ protected virtual IObservable PagesForViewModel(IRoutableViewModel? vm) var ret = ViewLocator.Current.ResolveView(vm); if (ret is null) { - var msg = $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{vm.GetType().Name}>"; + var msg = + $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{vm.GetType().Name}>"; - return Observable.Throw(new Exception(msg)); + return Observable.Throw(new InvalidOperationException(msg)); } ret.ViewModel = vm; @@ -229,8 +142,11 @@ protected virtual IObservable PagesForViewModel(IRoutableViewModel? vm) /// /// The vm. /// An observable of the page associated to a . - [RequiresUnreferencedCode("This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] - [RequiresDynamicCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), " + + "trimming can't validate that the requirements of those annotations are met.")] protected virtual Page PageForViewModel(IRoutableViewModel vm) { ArgumentNullException.ThrowIfNull(vm); @@ -238,9 +154,10 @@ protected virtual Page PageForViewModel(IRoutableViewModel vm) var ret = ViewLocator.Current.ResolveView(vm); if (ret is null) { - var msg = $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{vm.GetType().Name}>"; + var msg = + $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{vm.GetType().Name}>"; - throw new Exception(msg); + throw new InvalidOperationException(msg); } ret.ViewModel = vm; @@ -261,17 +178,20 @@ protected virtual Page PageForViewModel(IRoutableViewModel vm) protected void InvalidateCurrentViewModel() { var vm = Router?.GetCurrentViewModel(); - if (CurrentPage is IViewFor page && vm is not null) + if (CurrentPage is not IViewFor page || vm is null) { - if (page.ViewModel?.GetType() == vm.GetType()) - { - // don't replace view model if vm is null or an incompatible type. - page.ViewModel = vm; - } - else - { - this.Log().Info($"The view type '{page.GetType().FullName}' is not compatible with '{vm.GetType().FullName}' this was called by {_action}, the viewmodel was not invalidated"); - } + return; + } + + if (page.ViewModel?.GetType() == vm.GetType()) + { + // don't replace view model if vm is null or an incompatible type. + page.ViewModel = vm; + } + else + { + this.Log().Info( + $"The view type '{page.GetType().FullName}' is not compatible with '{vm.GetType().FullName}' this was called by {_action}, the viewmodel was not invalidated"); } } @@ -280,43 +200,201 @@ protected void InvalidateCurrentViewModel() /// to affect manipulations like Add or Clear. /// /// A representing the asynchronous operation. - [RequiresUnreferencedCode("This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] - [RequiresDynamicCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), " + + "trimming can't validate that the requirements of those annotations are met.")] protected async Task SyncNavigationStacksAsync() { - if (Navigation.NavigationStack.Count != Router.NavigationStack.Count - || StacksAreDifferent()) + if (Navigation.NavigationStack.Count == Router.NavigationStack.Count + && !StacksAreDifferent()) { - if (Navigation.NavigationStack.Count > 2) + return; + } + + if (Navigation.NavigationStack.Count > 2) + { + for (var i = Navigation.NavigationStack.Count - 2; i >= 0; i--) { - for (var i = Navigation.NavigationStack.Count - 2; i >= 0; i--) - { - Navigation.RemovePage(Navigation.NavigationStack[i]); - } + Navigation.RemovePage(Navigation.NavigationStack[i]); } + } - Page? rootPage; - if (Navigation.NavigationStack.Count >= 1) + Page? rootPage; + if (Navigation.NavigationStack.Count >= 1) + { + rootPage = Navigation.NavigationStack[0]; + } + else + { + rootPage = PageForViewModel(Router.NavigationStack[0]); + await Navigation.PushAsync(rootPage, false); + } + + if (Router.NavigationStack.Count >= 1) + { + for (var i = 0; i < Router.NavigationStack.Count - 1; i++) { - rootPage = Navigation.NavigationStack[0]; + var page = PageForViewModel(Router.NavigationStack[i]); + Navigation.InsertPageBefore(page, rootPage); } - else + } + } + + /// + /// Subscribes to changes and resyncs when the stack is cleared. + /// + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), " + + "trimming can't validate that the requirements of those annotations are met.")] + private void SubscribeToNavigationStackChanges() => + Observable.FromEventPattern( + x => Router.NavigationStack.CollectionChanged += x, + x => Router.NavigationStack.CollectionChanged -= x) + .Where(_ => !_currentlyNavigating && Router?.NavigationStack.Count == 0) + .Subscribe(async _ => await SyncNavigationStacksAsync()) + .DisposeWith(_subscriptions); + + /// + /// Subscribes to requests and pops the page accordingly. + /// + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), " + + "trimming can't validate that the requirements of those annotations are met.")] + private void SubscribeToNavigateBack() => + Router? + .NavigateBack + .Subscribe(async _ => { - rootPage = PageForViewModel(Router.NavigationStack[0]); - await Navigation.PushAsync(rootPage, false); - } + try + { + _currentlyNavigating = true; + await PopAsync(); + } + finally + { + _currentlyNavigating = false; + } - if (Router.NavigationStack.Count >= 1) + _action = "NavigatedBack"; + InvalidateCurrentViewModel(); + await SyncNavigationStacksAsync(); + }) + .DisposeWith(_subscriptions); + + /// + /// Subscribes to requests and pushes the resolved page. + /// + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), " + + "trimming can't validate that the requirements of those annotations are met.")] + private void SubscribeToNavigate() => + Router? + .Navigate + .Where(_ => StacksAreDifferent()) + .ObserveOn(RxSchedulers.MainThreadScheduler) + .SelectMany(_ => PagesForViewModel(Router.GetCurrentViewModel())) + .SelectMany(async page => { - for (var i = 0; i < Router.NavigationStack.Count - 1; i++) + var animated = true; + var attribute = page.GetType().GetCustomAttribute(); + if (attribute is not null) { - var page = PageForViewModel(Router.NavigationStack[i]); - Navigation.InsertPageBefore(page, rootPage); + animated = false; } - } - } + + try + { + _currentlyNavigating = true; + await PushAsync(page, animated); + } + finally + { + _currentlyNavigating = false; + } + + await SyncNavigationStacksAsync(); + + return page; + }) + .Subscribe() + .DisposeWith(_subscriptions); + + /// + /// Subscribes to the event to keep the router stack in sync + /// when the user navigates back via the application back button. + /// + private void SubscribeToPopped() + { + var poppingEvent = Observable.FromEvent, Unit>( + eventHandler => + { + void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + x => Popped += x, + x => Popped -= x); + + // NB: User pressed the Application back as opposed to requesting Back via Router.NavigateBack. + poppingEvent + .Where(_ => !_currentlyNavigating && Router is not null) + .Subscribe(_ => + { + if (Router?.NavigationStack.Count > 0) + { + Router.NavigationStack.RemoveAt(Router.NavigationStack.Count - 1); + } + + _action = "Popped"; + InvalidateCurrentViewModel(); + }) + .DisposeWith(_subscriptions); + } + + /// + /// Subscribes to the event to keep the router stack in sync + /// when the user pops back to the root page. + /// + private void SubscribeToPoppedToRoot() + { + var poppingToRootEvent = Observable.FromEvent, Unit>( + eventHandler => + { + void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + x => PoppedToRoot += x, + x => PoppedToRoot -= x); + + poppingToRootEvent + .Where(_ => !_currentlyNavigating && Router is not null) + .Subscribe(_ => + { + for (var i = Router?.NavigationStack.Count - 1; i > 0; i--) + { + if (i.HasValue) + { + Router?.NavigationStack.RemoveAt(i.Value); + } + } + + _action = "PoppedToRoot"; + InvalidateCurrentViewModel(); + }) + .DisposeWith(_subscriptions); } + /// + /// Determines whether the page navigation stack differs from the router navigation stack. + /// + /// if the stacks are different; otherwise, . private bool StacksAreDifferent() { for (var i = 0; i < Router.NavigationStack.Count; i++) diff --git a/src/ReactiveUI.Maui/RoutedViewHost{TViewModel}.cs b/src/ReactiveUI.Maui/RoutedViewHost{TViewModel}.cs index c2c3592d24..5c67bf5e88 100644 --- a/src/ReactiveUI.Maui/RoutedViewHost{TViewModel}.cs +++ b/src/ReactiveUI.Maui/RoutedViewHost{TViewModel}.cs @@ -1,11 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Specialized; using System.Reflection; - using Microsoft.Maui.Controls; namespace ReactiveUI.Maui; @@ -17,145 +16,55 @@ namespace ReactiveUI.Maui; /// The type of the view model. Must have a public parameterless constructor. /// /// -public partial class RoutedViewHost<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : NavigationPage, IActivatableView, IEnableLogger +public class RoutedViewHost< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : + NavigationPage, IActivatableView, IEnableLogger where TViewModel : class, IRoutableViewModel { /// /// The router bindable property. /// public static readonly BindableProperty RouterProperty = BindableProperty.Create( - nameof(Router), - typeof(RoutingState), - typeof(RoutedViewHost), - default(RoutingState)); + nameof(Router), + typeof(RoutingState), + typeof(RoutedViewHost)); /// /// The Set Title on Navigate property. /// public static readonly BindableProperty SetTitleOnNavigateProperty = BindableProperty.Create( - nameof(SetTitleOnNavigate), - typeof(bool), - typeof(RoutedViewHost), - false); + nameof(SetTitleOnNavigate), + typeof(bool), + typeof(RoutedViewHost), + false); + /// + /// The subscriptions created by this host. + /// private readonly CompositeDisposable _subscriptions = []; + + /// + /// The name of the last navigation action that occurred. + /// private string? _action; + + /// + /// A value indicating whether a navigation operation is currently in progress. + /// private bool _currentlyNavigating; /// /// Initializes a new instance of the class. /// - /// You *must* register an IScreen class representing your App's main Screen. + /// You *must* register an IScreen class representing your App's main Screen. public RoutedViewHost() { // Subscribe directly without WhenActivated - Observable.FromEventPattern( - x => Router!.NavigationStack.CollectionChanged += x, - x => Router!.NavigationStack.CollectionChanged -= x) - .Where(_ => !_currentlyNavigating && Router?.NavigationStack.Count == 0) - .Subscribe(async _ => await SyncNavigationStacksAsync()) - .DisposeWith(_subscriptions); - - Router? - .NavigateBack - .Subscribe(async _ => - { - try - { - _currentlyNavigating = true; - await PopAsync(); - } - finally - { - _currentlyNavigating = false; - } - - _action = "NavigatedBack"; - InvalidateCurrentViewModel(); - await SyncNavigationStacksAsync(); - }) - .DisposeWith(_subscriptions); - - Router? - .Navigate - .Where(_ => StacksAreDifferent()) - .ObserveOn(RxSchedulers.MainThreadScheduler) - .SelectMany(_ => PagesForViewModel(Router.GetCurrentViewModel())) - .SelectMany(async page => - { - var animated = true; - var attribute = page.GetType().GetCustomAttribute(); - if (attribute is not null) - { - animated = false; - } - - try - { - _currentlyNavigating = true; - await PushAsync(page, animated); - } - finally - { - _currentlyNavigating = false; - } - - await SyncNavigationStacksAsync(); - - return page; - }) - .Subscribe() - .DisposeWith(_subscriptions); - - var poppingEvent = Observable.FromEvent, Unit>( - eventHandler => - { - void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); - return Handler; - }, - x => Popped += x, - x => Popped -= x); - - // NB: User pressed the Application back as opposed to requesting Back via Router.NavigateBack. - poppingEvent - .Where(_ => !_currentlyNavigating && Router is not null) - .Subscribe(_ => - { - if (Router?.NavigationStack.Count > 0) - { - Router.NavigationStack.RemoveAt(Router.NavigationStack.Count - 1); - } - - _action = "Popped"; - InvalidateCurrentViewModel(); - }) - .DisposeWith(_subscriptions); - - var poppingToRootEvent = Observable.FromEvent, Unit>( - eventHandler => - { - void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); - return Handler; - }, - x => PoppedToRoot += x, - x => PoppedToRoot -= x); - - poppingToRootEvent - .Where(_ => !_currentlyNavigating && Router is not null) - .Subscribe(_ => - { - for (var i = Router?.NavigationStack.Count - 1; i > 0; i--) - { - if (i.HasValue) - { - Router?.NavigationStack.RemoveAt(i.Value); - } - } - - _action = "PoppedToRoot"; - InvalidateCurrentViewModel(); - }) - .DisposeWith(_subscriptions); + SubscribeToNavigationStackChanges(); + SubscribeToNavigateBack(); + SubscribeToNavigate(); + SubscribeToPopped(); + SubscribeToPoppedToRoot(); // Perform initial sync asynchronously _ = Task.Run(async () => @@ -170,7 +79,8 @@ public RoutedViewHost() } }); - var screen = AppLocator.Current.GetService() ?? throw new Exception("You *must* register an IScreen class representing your App's main Screen"); + var screen = AppLocator.Current.GetService() ?? + throw new InvalidOperationException("You *must* register an IScreen class representing your App's main Screen"); Router = screen.Router; } @@ -208,9 +118,10 @@ protected virtual IObservable PagesForViewModel(IRoutableViewModel? vm) var ret = ViewLocator.Current.ResolveView(); if (ret is null) { - var msg = $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{typeof(TViewModel).Name}>"; + var msg = + $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{typeof(TViewModel).Name}>"; - return Observable.Throw(new Exception(msg)); + return Observable.Throw(new InvalidOperationException(msg)); } ret.ViewModel = vm as TViewModel; @@ -237,9 +148,10 @@ protected virtual Page PageForViewModel(IRoutableViewModel vm) var ret = ViewLocator.Current.ResolveView(); if (ret is null) { - var msg = $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{typeof(TViewModel).Name}>"; + var msg = + $"Couldn't find a View for ViewModel. You probably need to register an IViewFor<{typeof(TViewModel).Name}>"; - throw new Exception(msg); + throw new InvalidOperationException(msg); } ret.ViewModel = vm as TViewModel; @@ -260,17 +172,20 @@ protected virtual Page PageForViewModel(IRoutableViewModel vm) protected void InvalidateCurrentViewModel() { var vm = Router?.GetCurrentViewModel(); - if (CurrentPage is IViewFor page && vm is not null) + if (CurrentPage is not IViewFor page || vm is null) { - if (page.ViewModel?.GetType() == vm.GetType()) - { - // don't replace view model if vm is null or an incompatible type. - page.ViewModel = vm; - } - else - { - this.Log().Info($"The view type '{page.GetType().FullName}' is not compatible with '{vm.GetType().FullName}' this was called by {_action}, the viewmodel was not invalidated"); - } + return; + } + + if (page.ViewModel?.GetType() == vm.GetType()) + { + // don't replace view model if vm is null or an incompatible type. + page.ViewModel = vm; + } + else + { + this.Log().Info( + $"The view type '{page.GetType().FullName}' is not compatible with '{vm.GetType().FullName}' this was called by {_action}, the viewmodel was not invalidated"); } } @@ -281,39 +196,179 @@ protected void InvalidateCurrentViewModel() /// A representing the asynchronous operation. protected async Task SyncNavigationStacksAsync() { - if (Navigation.NavigationStack.Count != Router.NavigationStack.Count - || StacksAreDifferent()) + if (Navigation.NavigationStack.Count == Router.NavigationStack.Count + && !StacksAreDifferent()) { - if (Navigation.NavigationStack.Count > 2) + return; + } + + if (Navigation.NavigationStack.Count > 2) + { + for (var i = Navigation.NavigationStack.Count - 2; i >= 0; i--) { - for (var i = Navigation.NavigationStack.Count - 2; i >= 0; i--) - { - Navigation.RemovePage(Navigation.NavigationStack[i]); - } + Navigation.RemovePage(Navigation.NavigationStack[i]); } + } - Page? rootPage; - if (Navigation.NavigationStack.Count >= 1) + Page? rootPage; + if (Navigation.NavigationStack.Count >= 1) + { + rootPage = Navigation.NavigationStack[0]; + } + else + { + rootPage = PageForViewModel(Router.NavigationStack[0]); + await Navigation.PushAsync(rootPage, false); + } + + if (Router.NavigationStack.Count >= 1) + { + for (var i = 0; i < Router.NavigationStack.Count - 1; i++) { - rootPage = Navigation.NavigationStack[0]; + var page = PageForViewModel(Router.NavigationStack[i]); + Navigation.InsertPageBefore(page, rootPage); } - else + } + } + + /// + /// Subscribes to changes and resyncs when the stack is cleared. + /// + private void SubscribeToNavigationStackChanges() => + Observable.FromEventPattern( + x => Router.NavigationStack.CollectionChanged += x, + x => Router.NavigationStack.CollectionChanged -= x) + .Where(_ => !_currentlyNavigating && Router?.NavigationStack.Count == 0) + .Subscribe(async _ => await SyncNavigationStacksAsync()) + .DisposeWith(_subscriptions); + + /// + /// Subscribes to requests and pops the page accordingly. + /// + private void SubscribeToNavigateBack() => + Router? + .NavigateBack + .Subscribe(async _ => { - rootPage = PageForViewModel(Router.NavigationStack[0]); - await Navigation.PushAsync(rootPage, false); - } + try + { + _currentlyNavigating = true; + await PopAsync(); + } + finally + { + _currentlyNavigating = false; + } - if (Router.NavigationStack.Count >= 1) + _action = "NavigatedBack"; + InvalidateCurrentViewModel(); + await SyncNavigationStacksAsync(); + }) + .DisposeWith(_subscriptions); + + /// + /// Subscribes to requests and pushes the resolved page. + /// + private void SubscribeToNavigate() => + Router? + .Navigate + .Where(_ => StacksAreDifferent()) + .ObserveOn(RxSchedulers.MainThreadScheduler) + .SelectMany(_ => PagesForViewModel(Router.GetCurrentViewModel())) + .SelectMany(async page => { - for (var i = 0; i < Router.NavigationStack.Count - 1; i++) + var animated = true; + var attribute = page.GetType().GetCustomAttribute(); + if (attribute is not null) { - var page = PageForViewModel(Router.NavigationStack[i]); - Navigation.InsertPageBefore(page, rootPage); + animated = false; } - } - } + + try + { + _currentlyNavigating = true; + await PushAsync(page, animated); + } + finally + { + _currentlyNavigating = false; + } + + await SyncNavigationStacksAsync(); + + return page; + }) + .Subscribe() + .DisposeWith(_subscriptions); + + /// + /// Subscribes to the event to keep the router stack in sync + /// when the user navigates back via the application back button. + /// + private void SubscribeToPopped() + { + var poppingEvent = Observable.FromEvent, Unit>( + eventHandler => + { + void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + x => Popped += x, + x => Popped -= x); + + // NB: User pressed the Application back as opposed to requesting Back via Router.NavigateBack. + poppingEvent + .Where(_ => !_currentlyNavigating && Router is not null) + .Subscribe(_ => + { + if (Router?.NavigationStack.Count > 0) + { + Router.NavigationStack.RemoveAt(Router.NavigationStack.Count - 1); + } + + _action = "Popped"; + InvalidateCurrentViewModel(); + }) + .DisposeWith(_subscriptions); + } + + /// + /// Subscribes to the event to keep the router stack in sync + /// when the user pops back to the root page. + /// + private void SubscribeToPoppedToRoot() + { + var poppingToRootEvent = Observable.FromEvent, Unit>( + eventHandler => + { + void Handler(object? sender, NavigationEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + x => PoppedToRoot += x, + x => PoppedToRoot -= x); + + poppingToRootEvent + .Where(_ => !_currentlyNavigating && Router is not null) + .Subscribe(_ => + { + for (var i = Router?.NavigationStack.Count - 1; i > 0; i--) + { + if (i.HasValue) + { + Router?.NavigationStack.RemoveAt(i.Value); + } + } + + _action = "PoppedToRoot"; + InvalidateCurrentViewModel(); + }) + .DisposeWith(_subscriptions); } + /// + /// Determines whether the page navigation stack differs from the router navigation stack. + /// + /// if the stacks are different; otherwise, . private bool StacksAreDifferent() { for (var i = 0; i < Router.NavigationStack.Count; i++) diff --git a/src/ReactiveUI.Maui/ViewModelViewHost.cs b/src/ReactiveUI.Maui/ViewModelViewHost.cs index 27ac208721..f598a6b484 100644 --- a/src/ReactiveUI.Maui/ViewModelViewHost.cs +++ b/src/ReactiveUI.Maui/ViewModelViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -12,35 +12,38 @@ namespace ReactiveUI.Maui; /// to be displayed should be assigned to the property. Optionally, the chosen view can be /// customized by specifying a contract via or . /// -[RequiresUnreferencedCode("This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] -[RequiresDynamicCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] -public partial class ViewModelViewHost : ContentView, IViewFor +[RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] +[RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic " + + "constraints), trimming can't validate that the requirements of those annotations are met.")] +public class ViewModelViewHost : ContentView, IViewFor { /// /// Identifies the property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(object), - typeof(ViewModelViewHost)); + nameof(ViewModel), + typeof(object), + typeof(ViewModelViewHost), + propertyChanged: OnViewModelPropertyChanged); /// /// Identifies the property. /// public static readonly BindableProperty DefaultContentProperty = BindableProperty.Create( - nameof(DefaultContent), - typeof(View), - typeof(ViewModelViewHost), - default(View)); + nameof(DefaultContent), + typeof(View), + typeof(ViewModelViewHost)); /// /// Identifies the property. /// public static readonly BindableProperty ViewContractObservableProperty = BindableProperty.Create( - nameof(ViewContractObservable), - typeof(IObservable), - typeof(ViewModelViewHost), - Observable.Never); + nameof(ViewContractObservable), + typeof(IObservable), + typeof(ViewModelViewHost), + Observable.Never); /// /// The ContractFallbackByPass dependency property. @@ -51,7 +54,14 @@ public partial class ViewModelViewHost : ContentView, IViewFor typeof(ViewModelViewHost), false); + /// + /// The disposables for the view resolution subscriptions. + /// private readonly CompositeDisposable _subscriptions = []; + + /// + /// The most recently observed view contract. + /// private string? _viewContract; /// @@ -68,23 +78,12 @@ public ViewModelViewHost() ViewContractObservable = Observable.Default; - // Observe ViewModel property changes without expression trees (AOT-friendly) - var viewModelChanged = MauiReactiveHelpers.CreatePropertyValueObservable( - this, - nameof(ViewModel), - () => ViewModel); - - // Combine ViewModel and ViewContractObservable streams - var vmAndContract = viewModelChanged.CombineLatest( - ViewContractObservable, - (vm, contract) => new { ViewModel = vm, Contract = contract }); - - // Subscribe directly without WhenActivated - vmAndContract - .Subscribe(x => + // Re-resolve when the contract changes; ViewModel changes are handled by OnViewModelPropertyChanged. + ViewContractObservable + .Subscribe(contract => { - _viewContract = x.Contract; - ResolveViewForViewModel(x.ViewModel, x.Contract); + _viewContract = contract; + ResolveViewForViewModel(ViewModel, contract); }) .DisposeWith(_subscriptions); } @@ -122,6 +121,10 @@ public IObservable ViewContractObservable /// /// This property is a mere convenience so that a fixed contract can be assigned directly in XAML. /// + [SuppressMessage( + "Critical Bug", + "S4275:Getters and setters should access the expected fields", + Justification = "Setter routes through ViewContractObservable; _viewContract is updated by its subscription.")] public string? ViewContract { get => _viewContract; @@ -147,8 +150,11 @@ public bool ContractFallbackByPass /// /// ViewModel. /// contract used by ViewLocator. - [RequiresUnreferencedCode("This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] - [RequiresDynamicCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic " + + "constraints), trimming can't validate that the requirements of those annotations are met.")] protected virtual void ResolveViewForViewModel(object? viewModel, string? contract) { if (viewModel is null) @@ -172,11 +178,28 @@ protected virtual void ResolveViewForViewModel(object? viewModel, string? contra if (viewInstance is not View castView) { - throw new InvalidOperationException($"View '{viewInstance.GetType().FullName}' is not a subclass of '{typeof(View).FullName}'."); + throw new InvalidOperationException( + $"View '{viewInstance.GetType().FullName}' is not a subclass of '{typeof(View).FullName}'."); } viewInstance.ViewModel = viewModel; Content = castView; } + + /// + /// Handles changes to the property by re-resolving the view for the new value. + /// + /// The object whose property changed. + /// The previous value. + /// The new value. + private static void OnViewModelPropertyChanged(BindableObject bindable, object? oldValue, object? newValue) + { + if (bindable is not ViewModelViewHost host || ModeDetector.InUnitTestRunner()) + { + return; + } + + host.ResolveViewForViewModel(newValue, host._viewContract); + } } diff --git a/src/ReactiveUI.Maui/ViewModelViewHost{TViewModel}.cs b/src/ReactiveUI.Maui/ViewModelViewHost{TViewModel}.cs index e0d4ad7d4c..785991b626 100644 --- a/src/ReactiveUI.Maui/ViewModelViewHost{TViewModel}.cs +++ b/src/ReactiveUI.Maui/ViewModelViewHost{TViewModel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,34 +17,36 @@ namespace ReactiveUI.Maui; /// This is the AOT-compatible generic version of ViewModelViewHost. It uses compile-time type information /// to resolve views without reflection, making it safe for Native AOT and trimming scenarios. /// -public partial class ViewModelViewHost<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TViewModel> : ContentView, IViewFor +public class ViewModelViewHost< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes + .PublicParameterlessConstructor)] + TViewModel> : ContentView, IViewFor where TViewModel : class { /// /// Identifies the property. /// public static readonly BindableProperty ViewModelProperty = BindableProperty.Create( - nameof(ViewModel), - typeof(TViewModel), - typeof(ViewModelViewHost)); + nameof(ViewModel), + typeof(TViewModel), + typeof(ViewModelViewHost)); /// /// Identifies the property. /// public static readonly BindableProperty DefaultContentProperty = BindableProperty.Create( - nameof(DefaultContent), - typeof(View), - typeof(ViewModelViewHost), - default(View)); + nameof(DefaultContent), + typeof(View), + typeof(ViewModelViewHost)); /// /// Identifies the property. /// public static readonly BindableProperty ViewContractObservableProperty = BindableProperty.Create( - nameof(ViewContractObservable), - typeof(IObservable), - typeof(ViewModelViewHost), - Observable.Never); + nameof(ViewContractObservable), + typeof(IObservable), + typeof(ViewModelViewHost), + Observable.Never); /// /// The ContractFallbackByPass dependency property. @@ -55,7 +57,14 @@ public partial class ViewModelViewHost<[DynamicallyAccessedMembers(DynamicallyAc typeof(ViewModelViewHost), false); + /// + /// The disposables for the view resolution subscriptions. + /// private readonly CompositeDisposable _subscriptions = []; + + /// + /// The most recently observed view contract. + /// private string? _viewContract; /// @@ -82,9 +91,7 @@ public TViewModel? ViewModel set => SetValue(ViewModelProperty, value); } - /// - /// Gets or sets the view model whose associated view is to be displayed. - /// + /// object? IViewFor.ViewModel { get => ViewModel; @@ -115,6 +122,10 @@ public IObservable ViewContractObservable /// /// This property is a mere convenience so that a fixed contract can be assigned directly in XAML. /// + [SuppressMessage( + "Critical Bug", + "S4275:Getters and setters should access the expected fields", + Justification = "Setter routes through ViewContractObservable; _viewContract is updated by its subscription.")] public string? ViewContract { get => _viewContract; @@ -171,7 +182,8 @@ protected virtual void ResolveViewForViewModel(TViewModel? viewModel, string? co if (viewInstance is not View castView) { - throw new InvalidOperationException($"View '{viewInstance.GetType().FullName}' is not a subclass of '{typeof(View).FullName}'."); + throw new InvalidOperationException( + $"View '{viewInstance.GetType().FullName}' is not a subclass of '{typeof(View).FullName}'."); } viewInstance.ViewModel = viewModel; diff --git a/src/ReactiveUI.Maui/WinUI/DependencyObjectObservableForProperty.cs b/src/ReactiveUI.Maui/WinUI/DependencyObjectObservableForProperty.cs index 09c3f88670..d737993e8b 100644 --- a/src/ReactiveUI.Maui/WinUI/DependencyObjectObservableForProperty.cs +++ b/src/ReactiveUI.Maui/WinUI/DependencyObjectObservableForProperty.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -21,11 +21,21 @@ namespace ReactiveUI; /// public class DependencyObjectObservableForProperty : ICreatesObservableForProperty { + /// + /// The binding affinity returned for objects that expose a matching DependencyProperty. + /// + private const int DependencyPropertyAffinity = 6; + /// [RequiresUnreferencedCode("GetAffinityForObject uses methods that may require unreferenced code")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + + /// + [RequiresUnreferencedCode("GetAffinityForObject uses methods that may require unreferenced code")] + public int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged) { - if (!typeof(DependencyObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + if (type is null || !typeof(DependencyObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { return 0; } @@ -35,12 +45,22 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang return 0; } - return 6; + return DependencyPropertyAffinity; } /// [RequiresUnreferencedCode("GetNotificationForProperty uses methods that may require unreferenced code")] - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + [RequiresUnreferencedCode("GetNotificationForProperty uses methods that may require unreferenced code")] + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + + /// + [RequiresUnreferencedCode("GetNotificationForProperty uses methods that may require unreferenced code")] + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings) { ArgumentNullException.ThrowIfNull(sender); @@ -87,6 +107,12 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang }); } + /// + /// Walks the type hierarchy to find a static property with the specified name. + /// + /// The type to start searching from. + /// The name of the property to locate. + /// The matching static , or if none is found. [RequiresUnreferencedCode("ActuallyGetProperty uses methods that may require unreferenced code")] private static PropertyInfo? ActuallyGetProperty(TypeInfo typeInfo, string propertyName) { @@ -105,6 +131,12 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang return null; } + /// + /// Walks the type hierarchy to find a static field with the specified name. + /// + /// The type to start searching from. + /// The name of the field to locate. + /// The matching static , or if none is found. [RequiresUnreferencedCode("ActuallyGetField uses methods that may require unreferenced code")] private static FieldInfo? ActuallyGetField(TypeInfo typeInfo, string propertyName) { @@ -123,6 +155,12 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang return null; } + /// + /// Builds a fetcher that resolves the backing the named property. + /// + /// The type that declares the property. + /// The name of the property whose backing DependencyProperty is required. + /// A function that returns the , or if it cannot be resolved. [RequiresUnreferencedCode("GetDependencyPropertyFetcher uses methods that may require unreferenced code")] private static Func? GetDependencyPropertyFetcher(Type type, string propertyName) { diff --git a/src/ReactiveUI.Maui/WinUI/DispatcherQueueScheduler.cs b/src/ReactiveUI.Maui/WinUI/DispatcherQueueScheduler.cs index efcea406f0..5a24155ea2 100644 --- a/src/ReactiveUI.Maui/WinUI/DispatcherQueueScheduler.cs +++ b/src/ReactiveUI.Maui/WinUI/DispatcherQueueScheduler.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // diff --git a/src/ReactiveUI.Maui/WinUI/TransitioningContentControl.Empty.cs b/src/ReactiveUI.Maui/WinUI/TransitioningContentControl.Empty.cs index 0fcc8657a9..5cd9af78b5 100644 --- a/src/ReactiveUI.Maui/WinUI/TransitioningContentControl.Empty.cs +++ b/src/ReactiveUI.Maui/WinUI/TransitioningContentControl.Empty.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Testing.Reactive/ReactiveUI.Testing.Reactive.csproj b/src/ReactiveUI.Testing.Reactive/ReactiveUI.Testing.Reactive.csproj index efca6b10d3..76818ac8c6 100644 --- a/src/ReactiveUI.Testing.Reactive/ReactiveUI.Testing.Reactive.csproj +++ b/src/ReactiveUI.Testing.Reactive/ReactiveUI.Testing.Reactive.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIModernTargets) ReactiveUI.Testing.Reactive @@ -10,13 +10,13 @@ true - - - - + + + + - - + + diff --git a/src/ReactiveUI.Testing.Reactive/TestSchedulerExtensions.cs b/src/ReactiveUI.Testing.Reactive/TestSchedulerExtensions.cs index 1369505f0d..4c57e212bc 100644 --- a/src/ReactiveUI.Testing.Reactive/TestSchedulerExtensions.cs +++ b/src/ReactiveUI.Testing.Reactive/TestSchedulerExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -70,7 +70,11 @@ public static Recorded> OnNextAt(this TestScheduler scheduler /// with. /// A recorded notification that can be provided to /// TestScheduler.CreateHotObservable. - public static Recorded> OnErrorAt(this TestScheduler scheduler, double milliseconds, Exception ex) => + [SuppressMessage("Major Code Smell", "S4018:Generic methods should provide type parameters", Justification = "T is the notification element type and cannot appear in the parameter list.")] + public static Recorded> OnErrorAt( + this TestScheduler scheduler, + double milliseconds, + Exception ex) => new( scheduler.FromTimeSpan(TimeSpan.FromMilliseconds(milliseconds)), Notification.CreateOnError(ex)); @@ -85,6 +89,7 @@ public static Recorded> OnErrorAt(this TestScheduler schedule /// on the recorded notification. /// A recorded notification that can be provided to /// TestScheduler.CreateHotObservable. + [SuppressMessage("Major Code Smell", "S4018:Generic methods should provide type parameters", Justification = "T is the notification element type and cannot appear in the parameter list.")] public static Recorded> OnCompletedAt(this TestScheduler scheduler, double milliseconds) => new( scheduler.FromTimeSpan(TimeSpan.FromMilliseconds(milliseconds)), diff --git a/src/ReactiveUI.Testing/AppBuilderTestBase.cs b/src/ReactiveUI.Testing/AppBuilderTestBase.cs index a06f9a6eaa..44d2023de1 100644 --- a/src/ReactiveUI.Testing/AppBuilderTestBase.cs +++ b/src/ReactiveUI.Testing/AppBuilderTestBase.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Testing/IBuilder.cs b/src/ReactiveUI.Testing/IBuilder.cs index 5525f773ac..899b33b8db 100644 --- a/src/ReactiveUI.Testing/IBuilder.cs +++ b/src/ReactiveUI.Testing/IBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -8,6 +8,5 @@ namespace ReactiveUI.Testing; /// /// An interface for building. /// -public interface IBuilder -{ -} +[SuppressMessage("Design", "CA1040:Avoid empty interfaces", Justification = "Marker interface constraining the fluent builder extensions.")] +public interface IBuilder; diff --git a/src/ReactiveUI.Testing/IBuilderExtensions.cs b/src/ReactiveUI.Testing/IBuilderExtensions.cs index 0bbf2dfd18..5100bde53f 100644 --- a/src/ReactiveUI.Testing/IBuilderExtensions.cs +++ b/src/ReactiveUI.Testing/IBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -8,6 +8,7 @@ namespace ReactiveUI.Testing; /// /// Default methods for the abstraction. /// +[SuppressMessage("Design", "CA1045:Do not pass types by reference", Justification = "The fluent builder mutates caller fields/collections in place by design.")] public static class IBuilderExtensions { /// diff --git a/src/ReactiveUI.Testing/MessageBusExtensions.cs b/src/ReactiveUI.Testing/MessageBusExtensions.cs index 1b2568076b..b0d5a30fda 100644 --- a/src/ReactiveUI.Testing/MessageBusExtensions.cs +++ b/src/ReactiveUI.Testing/MessageBusExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -10,6 +10,9 @@ namespace ReactiveUI.Testing; /// public static class MessageBusExtensions { + /// + /// The lock object used to serialize access to the shared message bus while it is overridden. + /// private static readonly object mbGate = 42; /// @@ -29,6 +32,21 @@ public static TRet With(this IMessageBus messageBus, Func block) } } + /// + /// Override the default Message Bus during the specified block. + /// + /// The message bus to use for the block. + /// The action to execute. + public static void With(this IMessageBus messageBus, Action block) + { + ArgumentExceptionHelper.ThrowIfNull(block); + + using (messageBus.WithMessageBus()) + { + block(); + } + } + /// /// WithMessageBus allows you to override the default Message Bus /// implementation until the object returned is disposed. If a @@ -50,19 +68,4 @@ public static IDisposable WithMessageBus(this IMessageBus messageBus) Monitor.Exit(mbGate); }); } - - /// - /// Override the default Message Bus during the specified block. - /// - /// The message bus to use for the block. - /// The action to execute. - public static void With(this IMessageBus messageBus, Action block) - { - ArgumentExceptionHelper.ThrowIfNull(block); - - using (messageBus.WithMessageBus()) - { - block(); - } - } } diff --git a/src/ReactiveUI.Testing/Properties/ReactiveUI.Testing.rd.xml b/src/ReactiveUI.Testing/Properties/ReactiveUI.Testing.rd.xml index c476061c29..e63255a220 100644 --- a/src/ReactiveUI.Testing/Properties/ReactiveUI.Testing.rd.xml +++ b/src/ReactiveUI.Testing/Properties/ReactiveUI.Testing.rd.xml @@ -33,7 +33,7 @@ See the LICENSE file in the project root for more information. - + diff --git a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj index a71d5427e5..56cb586ca9 100644 --- a/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj +++ b/src/ReactiveUI.Testing/ReactiveUI.Testing.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIModernTargets) ReactiveUI.Testing @@ -9,18 +9,18 @@ mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;test; - - - - - - - - - + + + + + + + + + - - + + - \ No newline at end of file + diff --git a/src/ReactiveUI.Testing/RxTest.cs b/src/ReactiveUI.Testing/RxTest.cs index 45b8d33e95..3319ffb7f2 100644 --- a/src/ReactiveUI.Testing/RxTest.cs +++ b/src/ReactiveUI.Testing/RxTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -12,8 +12,24 @@ namespace ReactiveUI.Testing; /// public static class RxTest { + /// + /// The default maximum wait in milliseconds used when none is supplied. + /// + private const int DefaultMaxWaitMs = 60000; + + /// + /// The global gate that serializes AppBuilder tests so they do not run concurrently. + /// private static readonly SemaphoreSlim TestGate = new(1, 1); + /// + /// Applications the builder test asynchronous. + /// + /// The test body. + /// testBody. + /// A representing the asynchronous operation. + public static Task AppBuilderTestAsync(Func testBody) => AppBuilderTestAsync(testBody, DefaultMaxWaitMs); + /// /// Applications the builder test asynchronous. /// @@ -21,7 +37,7 @@ public static class RxTest /// The maximum wait in milliseconds for both acquiring the test gate and running the test body. /// testBody. /// A representing the asynchronous operation. - public static async Task AppBuilderTestAsync(Func testBody, int maxWaitMs = 60000) + public static async Task AppBuilderTestAsync(Func testBody, int maxWaitMs) { ArgumentExceptionHelper.ThrowIfNull(testBody); diff --git a/src/ReactiveUI.Testing/SchedulerExtensions.cs b/src/ReactiveUI.Testing/SchedulerExtensions.cs index 0bfc6c3640..54408715c8 100644 --- a/src/ReactiveUI.Testing/SchedulerExtensions.cs +++ b/src/ReactiveUI.Testing/SchedulerExtensions.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Threading; - namespace ReactiveUI.Testing; /// @@ -61,6 +59,21 @@ public static TRet With(this T scheduler, Func block) return ret; } + /// + /// With is an extension method that uses the given scheduler as the + /// default Deferred and Taskpool schedulers for the given Action. + /// + /// The type. + /// The scheduler to use. + /// The action to execute. + public static void With(this T scheduler, Action block) + where T : IScheduler => + scheduler.With(x => + { + block(x); + return 0; + }); + /// /// With is an extension method that uses the given scheduler as the /// default Deferred and Taskpool schedulers for the given Func. Use @@ -86,21 +99,6 @@ public static async Task WithAsync(this T scheduler, Func - /// With is an extension method that uses the given scheduler as the - /// default Deferred and Taskpool schedulers for the given Action. - /// - /// The type. - /// The scheduler to use. - /// The action to execute. - public static void With(this T scheduler, Action block) - where T : IScheduler => - scheduler.With(x => - { - block(x); - return 0; - }); - /// /// With is an extension method that uses the given scheduler as the /// default Deferred and Taskpool schedulers for the given Action. diff --git a/src/ReactiveUI.Testing/TestSequencer.cs b/src/ReactiveUI.Testing/TestSequencer.cs index c52718b677..2670e19c00 100644 --- a/src/ReactiveUI.Testing/TestSequencer.cs +++ b/src/ReactiveUI.Testing/TestSequencer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,13 +11,25 @@ namespace ReactiveUI.Testing; /// public class TestSequencer : IDisposable { + /// + /// The number of participants that must reach the barrier before a phase advances. + /// + private const int ParticipantCount = 2; + + /// + /// The barrier used to synchronize the test phases between participants. + /// private readonly Barrier _phaseSync; + + /// + /// A value indicating whether this instance has already been disposed. + /// private bool _disposedValue; /// /// Initializes a new instance of the class. /// - public TestSequencer() => _phaseSync = new(2); + public TestSequencer() => _phaseSync = new(ParticipantCount); /// /// Gets the number of completed phases. @@ -35,6 +47,14 @@ public class TestSequencer : IDisposable /// public long CurrentPhase { get; private set; } + /// + /// Advances this phase instance. + /// + /// + /// A representing the asynchronous operation. + /// + public Task AdvancePhaseAsync() => AdvancePhaseAsync(string.Empty); + /// /// Advances this phase instance. /// @@ -42,7 +62,7 @@ public class TestSequencer : IDisposable /// /// A representing the asynchronous operation. /// - public async Task AdvancePhaseAsync(string comment = "") + public async Task AdvancePhaseAsync(string comment) { if (_phaseSync.ParticipantCount == _phaseSync.ParticipantsRemaining) { @@ -62,7 +82,7 @@ public async Task AdvancePhaseAsync(string comment = "") public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); + Dispose(true); GC.SuppressFinalize(this); } @@ -72,14 +92,16 @@ public void Dispose() /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { - if (!_disposedValue) + if (_disposedValue) { - if (disposing) - { - _phaseSync.Dispose(); - } + return; + } - _disposedValue = true; + if (disposing) + { + _phaseSync.Dispose(); } + + _disposedValue = true; } } diff --git a/src/ReactiveUI.WinUI/Builder/WinUIReactiveUIBuilderExtensions.cs b/src/ReactiveUI.WinUI/Builder/WinUIReactiveUIBuilderExtensions.cs index d6fdad6c30..3732cd62fc 100644 --- a/src/ReactiveUI.WinUI/Builder/WinUIReactiveUIBuilderExtensions.cs +++ b/src/ReactiveUI.WinUI/Builder/WinUIReactiveUIBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj b/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj index 30f51f2425..cbb80dcf6f 100644 --- a/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj +++ b/src/ReactiveUI.WinUI/ReactiveUI.WinUI.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIWinUITargets) 10.0.19041.0 @@ -6,7 +6,6 @@ ReactiveUI.WinUI.Desktop mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;winui true - $(NoWarn);NETSDK1206 IS_WINUI;WINUI_TARGET; win-x64;win-x86;win-arm64 true @@ -16,45 +15,45 @@ - - - + + + - + - + - - - - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - + - + diff --git a/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs b/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs index a18af86788..4a21df91bb 100644 --- a/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs +++ b/src/ReactiveUI.Winforms/ActivationForViewFetcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,80 +15,101 @@ namespace ReactiveUI.Winforms; /// public class ActivationForViewFetcher : IActivationForViewFetcher, IEnableLogger { + /// Caches whether the control is being used at design time. private bool? _isDesignModeCache; /// - public int GetAffinityForView(Type view) => typeof(Control).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; + public int GetAffinityForView(Type view) => + typeof(Control).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? BindingAffinity.ExactType : 0; /// public IObservable GetActivationForView(IActivatableView view) { - // Startup: Control.HandleCreated > Control.BindingContextChanged > Form.Load > Control.VisibleChanged > Form.Activated > Form.Shown - // Shutdown: Form.Closing > Form.FormClosing > Form.Closed > Form.FormClosed > Form.Deactivate - // https://docs.microsoft.com/en-us/dotnet/framework/winforms/order-of-events-in-windows-forms - // - // Note: A control is not yet set sited in its constructor. We must delay the design mode check until after one of the events fires. - if (view is Control control) + switch (view) { - var handleDestroyed = Observable.FromEvent( - eventHandler => (_, _) => eventHandler(false), - h => control.HandleDestroyed += h, - h => control.HandleDestroyed -= h); - - var handleCreated = Observable.FromEvent( - eventHandler => (_, _) => eventHandler(true), - h => control.HandleCreated += h, - h => control.HandleCreated -= h); - - var visibleChanged = Observable.FromEvent( - eventHandler => (_, _) => eventHandler(control.Visible), - h => control.VisibleChanged += h, - h => control.VisibleChanged -= h); - - IObservable controlActivation; - - if (view is Form form) - { - var formClosed = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, FormClosedEventArgs e) => eventHandler(control.Visible); - return Handler; - }, - h => form.FormClosed += h, - h => form.FormClosed -= h); - - controlActivation = Observable.Merge(handleDestroyed, handleCreated, visibleChanged, formClosed); - } - else - { - controlActivation = Observable.Merge(handleDestroyed, handleCreated, visibleChanged); - } - - return controlActivation.DistinctUntilChanged().Where(_ => !GetCachedIsDesignMode(control)); - } + // Startup: Control.HandleCreated > Control.BindingContextChanged > Form.Load > Control.VisibleChanged > Form.Activated > Form.Shown + // Shutdown: Form.Closing > Form.FormClosing > Form.Closed > Form.FormClosed > Form.Deactivate + // https://docs.microsoft.com/en-us/dotnet/framework/winforms/order-of-events-in-windows-forms + case Control control when GetCachedIsDesignMode(control): + break; + case Control control: + return GetActivationForControl(control); - if (view is null) - { - this.Log().Warn( - CultureInfo.InvariantCulture, - "Expected a view of type System.Windows.Forms.Control it was null"); + case null: + { + this.Log().Warn( + CultureInfo.InvariantCulture, + "Expected a view of type System.Windows.Forms.Control it was null"); + break; + } + + default: + { + // Show a friendly warning in the log that this view will never be activated + this.Log().Warn( + CultureInfo.InvariantCulture, + "Expected a view of type System.Windows.Forms.Control but it is {0}.\r\nYou need to implement your own IActivationForViewFetcher for {0}.", + view.GetType()); + break; + } } - else + + return Observable.Empty; + } + + /// Builds the activation observable for a WinForms control, including form lifecycle when applicable. + /// The control to observe. + /// An observable that signals when the control is activated and deactivated. + private static IObservable GetActivationForControl(Control control) + { + var handleDestroyed = Observable.FromEvent( + eventHandler => (_, _) => eventHandler(false), + h => control.HandleDestroyed += h, + h => control.HandleDestroyed -= h); + + var handleCreated = Observable.FromEvent( + eventHandler => (_, _) => eventHandler(true), + h => control.HandleCreated += h, + h => control.HandleCreated -= h); + + var visibleChanged = Observable.FromEvent( + eventHandler => (_, _) => eventHandler(control.Visible), + h => control.VisibleChanged += h, + h => control.VisibleChanged -= h); + + var controlActivation = Observable.Merge(handleDestroyed, handleCreated, visibleChanged) + .DistinctUntilChanged(); + + if (control is Form form) { - // Show a friendly warning in the log that this view will never be activated - this.Log().Warn( - CultureInfo.InvariantCulture, - "Expected a view of type System.Windows.Forms.Control but it is {0}.\r\nYou need to implement your own IActivationForViewFetcher for {0}.", - view.GetType()); + var formClosed = Observable.FromEvent( + eventHandler => + { + void Handler(object? sender, FormClosedEventArgs e) => eventHandler(control.Visible); + return Handler; + }, + h => form.FormClosed += h, + h => form.FormClosed -= h); + controlActivation = controlActivation.Merge(formClosed) + .DistinctUntilChanged(); } - return Observable.Empty; + return controlActivation; } + /// Determines whether the control is currently running at design time. + /// The control to inspect. + /// true if the control is in design mode; otherwise, false. + private static bool GetIsDesignMode(Control control) => + LicenseManager.UsageMode == LicenseUsageMode.Designtime || control.Site?.DesignMode == true || + control.Parent?.Site?.DesignMode == true; + + /// Gets the cached design-mode state for the control, computing it on first access. + /// The control to inspect. + /// true if the control is in design mode; otherwise, false. private bool GetCachedIsDesignMode(Control control) { - _isDesignModeCache ??= control.GetIsAncestorSiteInDesignMode(); + _isDesignModeCache ??= GetIsDesignMode(control); return _isDesignModeCache.Value; } diff --git a/src/ReactiveUI.Winforms/Builder/WinFormsReactiveUIBuilderExtensions.cs b/src/ReactiveUI.Winforms/Builder/WinFormsReactiveUIBuilderExtensions.cs index 7d546517fb..1866ef3d1b 100644 --- a/src/ReactiveUI.Winforms/Builder/WinFormsReactiveUIBuilderExtensions.cs +++ b/src/ReactiveUI.Winforms/Builder/WinFormsReactiveUIBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -16,7 +16,8 @@ public static class WinFormsReactiveUIBuilderExtensions /// /// The win forms main thread scheduler. /// - public static IScheduler WinFormsMainThreadScheduler { get; } = new WaitForDispatcherScheduler(static () => new SynchronizationContextScheduler(new WindowsFormsSynchronizationContext())); + public static IScheduler WinFormsMainThreadScheduler { get; } = new WaitForDispatcherScheduler(static () => + new SynchronizationContextScheduler(new WindowsFormsSynchronizationContext())); /// /// Configures ReactiveUI for WinForms platform with appropriate schedulers. @@ -24,8 +25,14 @@ public static class WinFormsReactiveUIBuilderExtensions /// The builder instance. /// The builder instance for chaining. #if NET6_0_OR_GREATER - [SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Not using reflection")] - [SuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "Not using reflection")] + [SuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "Not using reflection")] + [SuppressMessage( + "AOT", + "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "Not using reflection")] #endif public static IReactiveUIBuilder WithWinForms(this IReactiveUIBuilder builder) { diff --git a/src/ReactiveUI.Winforms/ContentControlBindingHook.cs b/src/ReactiveUI.Winforms/ContentControlBindingHook.cs index 0af41829c8..21eddb462e 100644 --- a/src/ReactiveUI.Winforms/ContentControlBindingHook.cs +++ b/src/ReactiveUI.Winforms/ContentControlBindingHook.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,7 +13,12 @@ namespace ReactiveUI.Winforms; public class ContentControlBindingHook : IPropertyBindingHook { /// - public bool ExecuteHook(object? source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction) + public bool ExecuteHook( + object? source, + object target, + Func[]> getCurrentViewModelProperties, + Func[]> getCurrentViewProperties, + BindingDirection direction) { ArgumentExceptionHelper.ThrowIfNull(getCurrentViewProperties); @@ -25,11 +30,11 @@ public bool ExecuteHook(object? source, object target, Func public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding { - // NB: These are in priority order + /// + /// The affinity returned when the target is a known Windows Forms control with an event target. + /// + private const int EventTargetAffinity = 6; + + /// + /// The affinity returned when the target exposes one of the conventional default events. + /// + private const int DefaultEventAffinity = 4; + + /// + /// The conventional default events to bind to, listed in priority order. + /// private static readonly List<(string name, Type type)> _defaultEventsToBind = [ ("Click", typeof(EventArgs)), - ("MouseUp", typeof(System.Windows.Forms.MouseEventArgs))]; + ("MouseUp", typeof(System.Windows.Forms.MouseEventArgs)) + ]; /// - public int GetAffinityForObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] T>(bool hasEventTarget) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public int GetAffinityForObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + T>(bool hasEventTarget) { var isWinformControl = typeof(Control).IsAssignableFrom(typeof(T)); if (isWinformControl) { - return 10; + return BindingAffinity.ExactType; } if (hasEventTarget) { - return 6; + return EventTargetAffinity; } - return _defaultEventsToBind.Any(static x => + return _defaultEventsToBind.Exists(static x => { - var ei = typeof(T).GetEvent(x.name, BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance); + var ei = typeof(T).GetEvent( + x.name, + BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance); return ei is not null; - }) ? 4 : 0; + }) + ? DefaultEventAffinity + : 0; } /// @@ -61,7 +85,11 @@ public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding /// An observable that supplies command parameter values. /// A disposable that unbinds the command, or null if no default event was found. /// Thrown when is . - public IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T>(ICommand? command, T? target, IObservable commandParameter) + public IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T>(ICommand? command, T? target, IObservable commandParameter) where T : class { ArgumentExceptionHelper.ThrowIfNull(target); @@ -89,8 +117,8 @@ public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding var type = typeof(T); var eventInfo = _defaultEventsToBind - .Select(x => new { EventInfo = type.GetEvent(x.name, bf), Args = x.type }) - .FirstOrDefault(x => x.EventInfo is not null); + .Select(x => new { EventInfo = type.GetEvent(x.name, bf), Args = x.type }) + .FirstOrDefault(x => x.EventInfo is not null); if (eventInfo is null) { @@ -104,14 +132,26 @@ public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding } else if (eventInfo.Args == typeof(System.Windows.Forms.MouseEventArgs)) { - return BindCommandToObject(command, target, commandParameter, eventInfo.EventInfo?.Name!); + return BindCommandToObject( + command, + target, + commandParameter, + eventInfo.EventInfo?.Name!); } return null; } /// - public IDisposable? BindCommandToObject(ICommand? command, T? target, IObservable commandParameter, string eventName) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IDisposable? BindCommandToObject( + ICommand? command, + T? target, + IObservable commandParameter, + string eventName) where T : class { ArgumentExceptionHelper.ThrowIfNull(command); @@ -127,10 +167,12 @@ public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding var evt = Observable.FromEventPattern(target, eventName); ret.Add(evt.Subscribe(_ => { - if (command.CanExecute(latestParameter)) + if (!command.CanExecute(latestParameter)) { - command.Execute(latestParameter); + return; } + + command.Execute(latestParameter); })); // We initially only accepted Controls here, but this is too restrictive: @@ -144,11 +186,11 @@ public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding if (enabledProperty is not null) { ret.Add(Observable.FromEvent( - eventHandler => (_, _) => eventHandler(command.CanExecute(latestParameter)), - x => command.CanExecuteChanged += x, - x => command.CanExecuteChanged -= x) - .StartWith(command.CanExecute(latestParameter)) - .Subscribe(x => enabledProperty.SetValue(target, x, null))); + eventHandler => (_, _) => eventHandler(command.CanExecute(latestParameter)), + x => command.CanExecuteChanged += x, + x => command.CanExecuteChanged -= x) + .StartWith(command.CanExecute(latestParameter)) + .Subscribe(x => enabledProperty.SetValue(target, x, null))); } } @@ -168,7 +210,11 @@ public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding /// Action that unsubscribes an event handler from the target event. /// A disposable that unbinds the command. /// Thrown when , , or is . - public IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T, TEventArgs>( + public IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T, TEventArgs>( ICommand? command, T? target, IObservable commandParameter, @@ -191,16 +237,15 @@ public sealed class CreatesWinformsCommandBinding : ICreatesCommandBinding void Handler(object? s, TEventArgs e) { var param = Volatile.Read(ref latestParameter); - if (command.CanExecute(param)) + if (!command.CanExecute(param)) { - command.Execute(param); + return; } + + command.Execute(param); } - var ret = new CompositeDisposable - { - commandParameter.Subscribe(x => Volatile.Write(ref latestParameter, x)) - }; + var ret = new CompositeDisposable { commandParameter.Subscribe(x => Volatile.Write(ref latestParameter, x)) }; addHandler(Handler); ret.Add(Disposable.Create(() => removeHandler(Handler))); @@ -214,11 +259,11 @@ void Handler(object? s, TEventArgs e) if (enabledProperty is not null) { ret.Add(Observable.FromEvent( - eventHandler => (_, _) => eventHandler(command.CanExecute(Volatile.Read(ref latestParameter))), - x => command.CanExecuteChanged += x, - x => command.CanExecuteChanged -= x) - .StartWith(command.CanExecute(latestParameter)) - .Subscribe(x => enabledProperty.SetValue(target, x, null))); + eventHandler => (_, _) => eventHandler(command.CanExecute(Volatile.Read(ref latestParameter))), + x => command.CanExecuteChanged += x, + x => command.CanExecuteChanged -= x) + .StartWith(command.CanExecute(latestParameter)) + .Subscribe(x => enabledProperty.SetValue(target, x, null))); } } @@ -237,7 +282,11 @@ void Handler(object? s, TEventArgs e) /// Action that unsubscribes an event handler from the target event. /// A disposable that unbinds the command. /// Thrown when , , or is . - public IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T>( + public IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T>( ICommand? command, T? target, IObservable commandParameter, @@ -259,16 +308,15 @@ void Handler(object? s, TEventArgs e) void Handler(object? s, EventArgs e) { var param = Volatile.Read(ref latestParameter); - if (command.CanExecute(param)) + if (!command.CanExecute(param)) { - command.Execute(param); + return; } + + command.Execute(param); } - var ret = new CompositeDisposable - { - commandParameter.Subscribe(x => Volatile.Write(ref latestParameter, x)) - }; + var ret = new CompositeDisposable { commandParameter.Subscribe(x => Volatile.Write(ref latestParameter, x)) }; addHandler(Handler); ret.Add(Disposable.Create(() => removeHandler(Handler))); @@ -282,11 +330,11 @@ void Handler(object? s, EventArgs e) if (enabledProperty is not null) { ret.Add(Observable.FromEvent( - eventHandler => (_, _) => eventHandler(command.CanExecute(Volatile.Read(ref latestParameter))), - x => command.CanExecuteChanged += x, - x => command.CanExecuteChanged -= x) - .StartWith(command.CanExecute(latestParameter)) - .Subscribe(x => enabledProperty.SetValue(target, x, null))); + eventHandler => (_, _) => eventHandler(command.CanExecute(Volatile.Read(ref latestParameter))), + x => command.CanExecuteChanged += x, + x => command.CanExecuteChanged -= x) + .StartWith(command.CanExecute(latestParameter)) + .Subscribe(x => enabledProperty.SetValue(target, x, null))); } } diff --git a/src/ReactiveUI.Winforms/ObservableCollectionChangedToListChangedTransformer.cs b/src/ReactiveUI.Winforms/ObservableCollectionChangedToListChangedTransformer.cs index 8db266cfcd..329ad57dc1 100644 --- a/src/ReactiveUI.Winforms/ObservableCollectionChangedToListChangedTransformer.cs +++ b/src/ReactiveUI.Winforms/ObservableCollectionChangedToListChangedTransformer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -7,6 +7,7 @@ namespace ReactiveUI.Winforms; +/// Provides conversions from collection-changed notifications to list-changed notifications. internal static class ObservableCollectionChangedToListChangedTransformer { /// @@ -19,12 +20,16 @@ internal static IEnumerable AsListChangedEventArgs(this No switch (ea.Action) { case NotifyCollectionChangedAction.Reset: - yield return new(ListChangedType.Reset, -1); - break; + { + yield return new(ListChangedType.Reset, -1); + break; + } case NotifyCollectionChangedAction.Replace: - yield return new(ListChangedType.ItemChanged, ea.NewStartingIndex); - break; + { + yield return new(ListChangedType.ItemChanged, ea.NewStartingIndex); + break; + } case NotifyCollectionChangedAction.Remove when ea.OldItems is not null: { @@ -47,12 +52,14 @@ internal static IEnumerable AsListChangedEventArgs(this No } case NotifyCollectionChangedAction.Move: - // https://msdn.microsoft.com/en-us/library/acskc6xz(v=vs.110).aspx - yield return new( - ListChangedType.ItemMoved, - ea.NewStartingIndex, - ea.OldStartingIndex); - break; + { + // https://msdn.microsoft.com/en-us/library/acskc6xz(v=vs.110).aspx + yield return new( + ListChangedType.ItemMoved, + ea.NewStartingIndex, + ea.OldStartingIndex); + break; + } } } } diff --git a/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs b/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs index c6551b8a6c..ff6c6c4c0d 100644 --- a/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs +++ b/src/ReactiveUI.Winforms/PanelSetMethodBindingConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -10,17 +10,22 @@ namespace ReactiveUI.Winforms; /// public class PanelSetMethodBindingConverter : ISetMethodBindingConverter { + /// The affinity returned when the source can be bound to a control collection. + private const int ControlCollectionAffinity = 10; + /// public int GetAffinityForObjects(Type? fromType, Type? toType) { - if (toType != typeof(Control.ControlCollection)) + if (toType != typeof(Control.ControlCollection) || fromType is null) { return 0; } - return fromType?.GetInterfaces().Any(static x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && x.GetGenericArguments()[0].IsSubclassOf(typeof(Control))) ?? false - ? 10 - : 0; + var implementsControlEnumerable = Array.Exists(fromType.GetInterfaces(), static x => + x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && + x.GetGenericArguments()[0].IsSubclassOf(typeof(Control))); + + return implementsControlEnumerable ? ControlCollectionAffinity : 0; } /// @@ -38,7 +43,7 @@ public object PerformSet(object? toTarget, object? newValue, object?[]? argument targetCollection.Owner.SuspendLayout(); targetCollection.Clear(); - targetCollection.AddRange(newValueEnumerable.ToArray()); + targetCollection.AddRange([.. newValueEnumerable]); targetCollection.Owner.ResumeLayout(); diff --git a/src/ReactiveUI.Winforms/PlatformOperations.cs b/src/ReactiveUI.Winforms/PlatformOperations.cs index 21eb364f7c..6dc68de006 100644 --- a/src/ReactiveUI.Winforms/PlatformOperations.cs +++ b/src/ReactiveUI.Winforms/PlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj b/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj index 92f17242f7..8bedd0804c 100644 --- a/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj +++ b/src/ReactiveUI.Winforms/ReactiveUI.Winforms.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIWindowsOnlyTargets) ReactiveUI.Winforms @@ -7,43 +7,42 @@ ReactiveUI.WinForms true mvvm;reactiveui;rx;reactive extensions;observable;LINQ;events;frp;winforms;net;net472; - $(NoWarn);IDE1006; True false false false - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + - + - + - - + + diff --git a/src/ReactiveUI.Winforms/ReactiveUserControl.Designer.cs b/src/ReactiveUI.Winforms/ReactiveUserControl.Designer.cs index bd779d10d5..c97a41ffe7 100644 --- a/src/ReactiveUI.Winforms/ReactiveUserControl.Designer.cs +++ b/src/ReactiveUI.Winforms/ReactiveUserControl.Designer.cs @@ -1,4 +1,4 @@ -namespace ReactiveUI.Winforms; +namespace ReactiveUI.Winforms; partial class ReactiveUserControl where TViewModel : class diff --git a/src/ReactiveUI.Winforms/ReactiveUserControl.cs b/src/ReactiveUI.Winforms/ReactiveUserControl.cs index f3425772b2..d095dc5b49 100644 --- a/src/ReactiveUI.Winforms/ReactiveUserControl.cs +++ b/src/ReactiveUI.Winforms/ReactiveUserControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.Designer.cs b/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.Designer.cs index 7648d0a303..71728e8154 100644 --- a/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.Designer.cs +++ b/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.Designer.cs @@ -1,4 +1,4 @@ -namespace ReactiveUI.Winforms; +namespace ReactiveUI.Winforms; partial class ReactiveUserControlNonGeneric { diff --git a/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.cs b/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.cs index 63c8ac8f4f..36e2f64f40 100644 --- a/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.cs +++ b/src/ReactiveUI.Winforms/ReactiveUserControlNonGeneric.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Winforms/Registrations.cs b/src/ReactiveUI.Winforms/Registrations.cs index 47d813b439..a38b4822cf 100644 --- a/src/ReactiveUI.Winforms/Registrations.cs +++ b/src/ReactiveUI.Winforms/Registrations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -18,19 +18,23 @@ public void Register(IRegistrar registrar) registrar.RegisterConstant(static () => new PlatformOperations()); registrar.RegisterConstant(static () => new CreatesWinformsCommandBinding()); - registrar.RegisterConstant(static () => new WinformsCreatesObservableForProperty()); + registrar.RegisterConstant(static () => + new WinformsCreatesObservableForProperty()); registrar.RegisterConstant(static () => new ActivationForViewFetcher()); registrar.RegisterConstant(static () => new PanelSetMethodBindingConverter()); - registrar.RegisterConstant(static () => new TableContentSetMethodBindingConverter()); + registrar.RegisterConstant(static () => + new TableContentSetMethodBindingConverter()); registrar.RegisterConstant(static () => new StringConverter()); registrar.RegisterConstant(static () => new SingleToStringTypeConverter()); registrar.RegisterConstant(static () => new DoubleToStringTypeConverter()); registrar.RegisterConstant(static () => new DecimalToStringTypeConverter()); registrar.RegisterConstant(static () => new ComponentModelFallbackConverter()); - if (!ModeDetector.InUnitTestRunner()) + if (ModeDetector.InUnitTestRunner()) { - WindowsFormsSynchronizationContext.AutoInstall = true; + return; } + + WindowsFormsSynchronizationContext.AutoInstall = true; } } diff --git a/src/ReactiveUI.Winforms/RoutedViewHost.Designer.cs b/src/ReactiveUI.Winforms/RoutedViewHost.Designer.cs index 22fca714d0..ea253ffe54 100644 --- a/src/ReactiveUI.Winforms/RoutedViewHost.Designer.cs +++ b/src/ReactiveUI.Winforms/RoutedViewHost.Designer.cs @@ -1,4 +1,4 @@ -namespace ReactiveUI.Winforms; +namespace ReactiveUI.Winforms; partial class RoutedControlHost { diff --git a/src/ReactiveUI.Winforms/RoutedViewHost.cs b/src/ReactiveUI.Winforms/RoutedViewHost.cs index eea62182c6..366fc49c77 100644 --- a/src/ReactiveUI.Winforms/RoutedViewHost.cs +++ b/src/ReactiveUI.Winforms/RoutedViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,16 @@ namespace ReactiveUI.Winforms; [DefaultProperty("ViewModel")] public partial class RoutedControlHost : UserControl, IReactiveObject { + /// Holds the subscriptions created during construction so they can be disposed together. private readonly CompositeDisposable _disposables = []; #pragma warning disable IDE0032 // Use auto property + /// Backing field for the routing state. private RoutingState? _router; + + /// Backing field for the default content shown when no view model is set. private Control? _defaultContent; + + /// Backing field for the view contract observable. private IObservable? _viewContractObservable; #pragma warning restore IDE0032 // Use auto property @@ -28,60 +34,62 @@ public RoutedControlHost() _disposables.Add(this.WhenAnyValue(nameof(DefaultContent)) .Subscribe(x => { - if (x is not null && Controls.Count == 0) + if (x is null || Controls.Count != 0) { - Controls.Add(InitView(x)); - components?.Add(DefaultContent); + return; } + + Controls.Add(InitView(x)); + components?.Add(DefaultContent); })); ViewContractObservable = Observable.Default; var vmAndContract = - this.WhenAnyObservable(x => x.Router!.CurrentViewModel!) + this.WhenAnyObservable(x => x.Router!.CurrentViewModel) .CombineLatest( - this.WhenAnyObservable(x => x.ViewContractObservable!), - (vm, contract) => new { ViewModel = vm, Contract = contract }); + this.WhenAnyObservable(x => x.ViewContractObservable!), + (vm, contract) => new { ViewModel = vm, Contract = contract }); Control? viewLastAdded = null; _disposables.Add(vmAndContract.Subscribe( - x => - { - // clear all hosted controls (view or default content) - SuspendLayout(); - Controls.Clear(); - - viewLastAdded?.Dispose(); - - if (x.ViewModel is null) - { - if (DefaultContent is not null) - { - InitView(DefaultContent); - Controls.Add(DefaultContent); - } - - ResumeLayout(); - return; - } - - var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current; - var view = viewLocator.ResolveView(x.ViewModel, x.Contract); - if (view is not null) - { - view.ViewModel = x.ViewModel; - - viewLastAdded = InitView((Control)view); - } - - if (viewLastAdded is not null) - { - Controls.Add(viewLastAdded); - } - - ResumeLayout(); - }, - RxState.DefaultExceptionHandler!.OnNext)); + x => + { + // clear all hosted controls (view or default content) + SuspendLayout(); + Controls.Clear(); + + viewLastAdded?.Dispose(); + + if (x.ViewModel is null) + { + if (DefaultContent is not null) + { + InitView(DefaultContent); + Controls.Add(DefaultContent); + } + + ResumeLayout(); + return; + } + + var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current; + var view = viewLocator.ResolveView(x.ViewModel, x.Contract); + if (view is not null) + { + view.ViewModel = x.ViewModel; + + viewLastAdded = InitView((Control)view); + } + + if (viewLastAdded is not null) + { + Controls.Add(viewLastAdded); + } + + ResumeLayout(); + }, + RxState.DefaultExceptionHandler.OnNext)); } /// @@ -156,6 +164,11 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + /// + /// Prepares a control for hosting by docking it to fill the host. + /// + /// The control to initialize. + /// The same control, docked to fill. private static Control InitView(Control view) { view.Dock = DockStyle.Fill; diff --git a/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs b/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs index fa879f381a..416439c788 100644 --- a/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs +++ b/src/ReactiveUI.Winforms/TableContentSetMethodBindingConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -10,13 +10,23 @@ namespace ReactiveUI.Winforms; /// public class TableContentSetMethodBindingConverter : ISetMethodBindingConverter { + /// The affinity returned when the source can be bound to a control collection. + private const int ControlCollectionAffinity = 10; + /// - public int GetAffinityForObjects(Type? fromType, Type? toType) => - toType != typeof(TableLayoutControlCollection) - ? 0 - : fromType?.GetInterfaces().Any(static x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && x.GetGenericArguments()[0].IsSubclassOf(typeof(Control))) ?? false - ? 10 - : 0; + public int GetAffinityForObjects(Type? fromType, Type? toType) + { + if (toType != typeof(TableLayoutControlCollection) || fromType is null) + { + return 0; + } + + var implementsControlEnumerable = Array.Exists(fromType.GetInterfaces(), static x => + x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) && + x.GetGenericArguments()[0].IsSubclassOf(typeof(Control))); + + return implementsControlEnumerable ? ControlCollectionAffinity : 0; + } /// public object PerformSet(object? toTarget, object? newValue, object?[]? arguments) @@ -25,7 +35,9 @@ public object PerformSet(object? toTarget, object? newValue, object?[]? argument if (toTarget is not TableLayoutControlCollection targetCollection) { - throw new ArgumentException($"{nameof(toTarget)} must be of type {nameof(TableLayoutControlCollection)}", nameof(toTarget)); + throw new ArgumentException( + $"{nameof(toTarget)} must be of type {nameof(TableLayoutControlCollection)}", + nameof(toTarget)); } if (newValue is not IEnumerable newValueEnumerable) @@ -37,7 +49,7 @@ public object PerformSet(object? toTarget, object? newValue, object?[]? argument targetCollection.Clear(); - targetCollection.AddRange(newValueEnumerable.ToArray()); + targetCollection.AddRange([.. newValueEnumerable]); targetCollection.Container.ResumeLayout(); return targetCollection; diff --git a/src/ReactiveUI.Winforms/ViewModelViewHost.Designer.cs b/src/ReactiveUI.Winforms/ViewModelViewHost.Designer.cs index d5d693d40d..a2a0c1058e 100644 --- a/src/ReactiveUI.Winforms/ViewModelViewHost.Designer.cs +++ b/src/ReactiveUI.Winforms/ViewModelViewHost.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 diff --git a/src/ReactiveUI.Winforms/ViewModelViewHost.cs b/src/ReactiveUI.Winforms/ViewModelViewHost.cs index 4e7306fea0..9d3727a055 100644 --- a/src/ReactiveUI.Winforms/ViewModelViewHost.cs +++ b/src/ReactiveUI.Winforms/ViewModelViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,17 +13,27 @@ namespace ReactiveUI.Winforms; /// For AOT-compatible scenarios, use ViewModelControlHost<TViewModel> instead. /// [DefaultProperty("ViewModel")] -[RequiresUnreferencedCode("This class uses reflection to determine view model types at runtime through ViewLocator, which may be incompatible with trimming.")] +[RequiresUnreferencedCode( + "This class uses reflection to determine view model types at runtime through ViewLocator, which may be incompatible with trimming.")] [RequiresDynamicCode("ViewLocator.ResolveView uses reflection which is incompatible with AOT compilation.")] public partial class ViewModelControlHost : UserControl, IReactiveObject, IViewFor { + /// Holds the subscriptions created during setup so they can be disposed together. private readonly CompositeDisposable _disposables = []; #pragma warning disable IDE0032 // Use auto property + /// Backing field for the default content shown when no view model is set. private Control? _defaultContent; + + /// Backing field for the view contract observable. private IObservable? _viewContractObservable; + + /// Backing field for the hosted view model. private object? _viewModel; + + /// Backing field for the currently displayed content. private object? _content; #pragma warning restore IDE0032 // Use auto property + /// Backing field indicating whether resolved views are cached and reused. private bool _cacheViews; /// @@ -143,37 +153,28 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + /// + /// Creates the subscriptions that keep the hosted view in sync with the view model and content. + /// + /// The disposables representing the active subscriptions. private IEnumerable SetupBindings() { var viewChanges = this.WhenAnyValue(nameof(Content)) .WhereNotNull() .OfType() - .Subscribe(x => - { - // change the view in the ui - SuspendLayout(); - - // clear out existing visible control view - foreach (Control? c in Controls) - { - c?.Dispose(); - Controls.Remove(c); - } + .Subscribe(SwapHostedView); - x!.Dock = DockStyle.Fill; - Controls.Add(x); - ResumeLayout(); - }); - - yield return viewChanges!; + yield return viewChanges; yield return this.WhenAnyValue(nameof(DefaultContent)).Subscribe(x => { - if (x is not null) + if (x is null) { - Content = DefaultContent; + return; } + + Content = DefaultContent; }); ViewContractObservable = Observable.Return(string.Empty); @@ -181,46 +182,91 @@ private IEnumerable SetupBindings() var vmAndContract = this.WhenAnyValue(nameof(ViewModel)) .CombineLatest( - this.WhenAnyObservable(x => x.ViewContractObservable!), - (vm, contract) => new { ViewModel = vm, Contract = contract }); + this.WhenAnyObservable(x => x.ViewContractObservable!), + (vm, contract) => new { ViewModel = vm, Contract = contract }); yield return vmAndContract.Subscribe( - x => - { - // set content to default when viewmodel is null - if (ViewModel is null) - { - if (DefaultContent is not null) - { - Content = DefaultContent; - } - - return; - } - - if (CacheViews) - { - // when caching views, check the current viewmodel and type - var c = _content as IViewFor; - - if (c?.ViewModel is not null && c.ViewModel.GetType() == x.ViewModel!.GetType()) - { - c.ViewModel = x.ViewModel; - - // return early here after setting the viewmodel - // allowing the view to update it's bindings - return; - } - } - - var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current; - var view = viewLocator.ResolveView(x.ViewModel, x.Contract); - if (view is not null) - { - view.ViewModel = x.ViewModel; - Content = view; - } - }, - RxState.DefaultExceptionHandler!.OnNext); + x => UpdateContentForViewModel(x.ViewModel, x.Contract), + RxState.DefaultExceptionHandler.OnNext); + } + + /// + /// Replaces the currently visible control with the supplied view, docked to fill the host. + /// + /// The control to display. + private void SwapHostedView(Control view) + { + // change the view in the ui + SuspendLayout(); + + // clear out existing visible control view + foreach (Control? c in Controls) + { + c?.Dispose(); + Controls.Remove(c); + } + + view.Dock = DockStyle.Fill; + Controls.Add(view); + ResumeLayout(); + } + + /// + /// Resolves and displays the content for the supplied view model and contract. + /// + /// The current view model, or null. + /// The view contract. + private void UpdateContentForViewModel(object? viewModel, string contract) + { + // set content to default when viewmodel is null + if (viewModel is null) + { + if (DefaultContent is not null) + { + Content = DefaultContent; + } + + return; + } + + if (TryReuseCachedView(viewModel)) + { + return; + } + + var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current; + var view = viewLocator.ResolveView(viewModel, contract); + if (view is null) + { + return; + } + + view.ViewModel = viewModel; + Content = view; + } + + /// + /// Reuses the cached view for the supplied view model when caching is enabled and the types match. + /// + /// The current view model. + /// true if the cached view was reused; otherwise, false. + private bool TryReuseCachedView(object viewModel) + { + if (!CacheViews) + { + return false; + } + + // when caching views, check the current viewmodel and type + var c = _content as IViewFor; + + if (c?.ViewModel is null || c.ViewModel.GetType() != viewModel.GetType()) + { + return false; + } + + // setting the viewmodel allows the view to update its bindings + c.ViewModel = viewModel; + return true; } } diff --git a/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs b/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs index cd36ed9012..d33c1b820a 100644 --- a/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs +++ b/src/ReactiveUI.Winforms/WinformsCreatesObservableForProperty.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,14 +14,30 @@ namespace ReactiveUI.Winforms; /// public class WinformsCreatesObservableForProperty : ICreatesObservableForProperty { + /// The affinity returned when a matching change event is found for a property. + private const int EventBindingAffinity = 8; + + /// Caches the reflected change event for each property to avoid repeated reflection. private static readonly MemoizingMRUCache<(Type type, string name), EventInfo?> EventInfoCache = new( - static (pair, _) => pair.type.GetEvent(pair.name + "Changed", BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public), - RxCacheSize.SmallCacheLimit); + static (pair, _) => pair.type.GetEvent( + pair.name + "Changed", + BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public), + RxCacheSize.SmallCacheLimit); /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged) { + if (type is null) + { + return 0; + } + var supportsTypeBinding = typeof(Component).IsAssignableFrom(type); if (!supportsTypeBinding) { @@ -29,41 +45,64 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang } var ei = EventInfoCache.Get((type, propertyName)); - return !beforeChanged && ei is not null ? 8 : 0; + return !beforeChanged && ei is not null ? EventBindingAffinity : 0; } /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged, + bool suppressWarnings) { ArgumentExceptionHelper.ThrowIfNull(sender); - var ei = EventInfoCache.Get((sender.GetType(), propertyName)) ?? throw new InvalidOperationException("Could not find a valid event for expression."); + var ei = EventInfoCache.Get((sender.GetType(), propertyName)) ?? + throw new InvalidOperationException("Could not find a valid event for expression."); return Observable.Create>(subj => + { + var completed = false; + var handler = new EventHandler((_, _) => + { + if (completed) { - var completed = false; - var handler = new EventHandler((o, e) => - { - if (completed) - { - return; - } + return; + } - try - { - subj.OnNext(new ObservedChange(sender, expression, default)); - } - catch (Exception ex) - { - subj.OnError(ex); - completed = true; - } - }); + try + { + subj.OnNext(new ObservedChange(sender, expression, null)); + } + catch (Exception ex) + { + subj.OnError(ex); + completed = true; + } + }); - var scheduler = RxSchedulers.MainThreadScheduler; + var scheduler = RxSchedulers.MainThreadScheduler; - ei.AddEventHandler(sender, handler); - return Disposable.Create(() => scheduler.Schedule(() => ei.RemoveEventHandler(sender, handler))); - }); + ei.AddEventHandler(sender, handler); + return Disposable.Create(() => scheduler.Schedule(() => ei.RemoveEventHandler(sender, handler))); + }); } } diff --git a/src/ReactiveUI.Wpf/ActivationForViewFetcher.cs b/src/ReactiveUI.Wpf/ActivationForViewFetcher.cs index ff81f99023..afda4beeee 100644 --- a/src/ReactiveUI.Wpf/ActivationForViewFetcher.cs +++ b/src/ReactiveUI.Wpf/ActivationForViewFetcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,8 +15,12 @@ namespace ReactiveUI; /// public class ActivationForViewFetcher : IActivationForViewFetcher { + /// The affinity returned when the view is not supported. + private const int NoAffinity = 0; + /// - public int GetAffinityForView(Type view) => typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; + public int GetAffinityForView(Type view) => + typeof(FrameworkElement).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? BindingAffinity.ExactType : NoAffinity; /// public IObservable GetActivationForView(IActivatableView view) @@ -36,13 +40,13 @@ public IObservable GetActivationForView(IActivatableView view) x => fe.Loaded -= x); var hitTestVisible = Observable.FromEvent( - eventHandler => - { - void Handler(object sender, DependencyPropertyChangedEventArgs e) => eventHandler((bool)e.NewValue); - return Handler; - }, - x => fe.IsHitTestVisibleChanged += x, - x => fe.IsHitTestVisibleChanged -= x); + eventHandler => + { + void Handler(object sender, DependencyPropertyChangedEventArgs e) => eventHandler((bool)e.NewValue); + return Handler; + }, + x => fe.IsHitTestVisibleChanged += x, + x => fe.IsHitTestVisibleChanged -= x); var viewUnloaded = Observable.FromEvent( eventHandler => @@ -56,12 +60,15 @@ public IObservable GetActivationForView(IActivatableView view) var windowActivation = GetActivationForWindow(view); return viewLoaded - .Merge(viewUnloaded) - .Merge(hitTestVisible) - .Merge(windowActivation) - .DistinctUntilChanged(); + .Merge(viewUnloaded) + .Merge(hitTestVisible) + .Merge(windowActivation) + .DistinctUntilChanged(); } + /// Gets the activation observable for a Window, signalling false when it closes. + /// The view to observe. + /// An observable that emits activation state for the window. private static IObservable GetActivationForWindow(IActivatableView view) { if (view is not Window window) diff --git a/src/ReactiveUI.Wpf/Attributes.cs b/src/ReactiveUI.Wpf/Attributes.cs index b2e6544ec7..6961252419 100644 --- a/src/ReactiveUI.Wpf/Attributes.cs +++ b/src/ReactiveUI.Wpf/Attributes.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,13 +11,13 @@ [assembly: XmlnsDefinition("http://reactiveui.net", "ReactiveUI")] [assembly: ThemeInfo( - // where theme specific resource dictionaries are located - // (used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.None, + // where theme specific resource dictionaries are located + // (used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.None, - // where the generic resource dictionary is located - // (used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) - ResourceDictionaryLocation.SourceAssembly) + // where the generic resource dictionary is located + // (used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) + ResourceDictionaryLocation.SourceAssembly) ] diff --git a/src/ReactiveUI.Wpf/AutoSuspendHelper.cs b/src/ReactiveUI.Wpf/AutoSuspendHelper.cs index 25dbcde52b..e37bf4ed71 100644 --- a/src/ReactiveUI.Wpf/AutoSuspendHelper.cs +++ b/src/ReactiveUI.Wpf/AutoSuspendHelper.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -53,44 +53,44 @@ public AutoSuspendHelper(Application app) RxSuspension.SuspensionHost.IsLaunchingNew = Observable.FromEvent( - eventHandler => - { - void Handler(object sender, StartupEventArgs e) => eventHandler(Unit.Default); - return Handler; - }, - x => app.Startup += x, - x => app.Startup -= x); + eventHandler => + { + void Handler(object sender, StartupEventArgs e) => eventHandler(Unit.Default); + return Handler; + }, + x => app.Startup += x, + x => app.Startup -= x); RxSuspension.SuspensionHost.IsUnpausing = Observable.FromEvent( - eventHandler => (_, _) => eventHandler(Unit.Default), - x => app.Activated += x, - x => app.Activated -= x); + eventHandler => (_, _) => eventHandler(Unit.Default), + x => app.Activated += x, + x => app.Activated -= x); RxSuspension.SuspensionHost.IsResuming = Observable.Never; // NB: No way to tell OS that we need time to suspend, we have to // do it in-process var deactivated = Observable.FromEvent( - eventHandler => (_, _) => eventHandler(Unit.Default), - x => app.Deactivated += x, - x => app.Deactivated -= x); + eventHandler => (_, _) => eventHandler(Unit.Default), + x => app.Deactivated += x, + x => app.Deactivated -= x); var exit = Observable.FromEvent( - eventHandler => - { - void Handler(object sender, ExitEventArgs e) => eventHandler(Disposable.Empty); - return Handler; - }, - x => app.Exit += x, - x => app.Exit -= x); + eventHandler => + { + void Handler(object sender, ExitEventArgs e) => eventHandler(Disposable.Empty); + return Handler; + }, + x => app.Exit += x, + x => app.Exit -= x); RxSuspension.SuspensionHost.ShouldPersistState = exit.Merge( - deactivated - .SelectMany(_ => Observable.Timer(IdleTimeout, RxSchedulers.TaskpoolScheduler)) - .TakeUntil(RxSuspension.SuspensionHost.IsUnpausing) - .Repeat() - .Select(_ => Disposable.Empty)); + deactivated + .SelectMany(_ => Observable.Timer(IdleTimeout, RxSchedulers.TaskpoolScheduler)) + .TakeUntil(RxSuspension.SuspensionHost.IsUnpausing) + .Repeat() + .Select(_ => Disposable.Empty)); var untimelyDeath = new Subject(); AppDomain.CurrentDomain.UnhandledException += (_, _) => untimelyDeath.OnNext(Unit.Default); diff --git a/src/ReactiveUI.Wpf/Binding/ValidationBindingMixins.cs b/src/ReactiveUI.Wpf/Binding/ValidationBindingMixins.cs index 9ebc5e3d3a..ade1fd71f3 100644 --- a/src/ReactiveUI.Wpf/Binding/ValidationBindingMixins.cs +++ b/src/ReactiveUI.Wpf/Binding/ValidationBindingMixins.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Linq.Expressions; - using ReactiveUI.Wpf.Binding; namespace ReactiveUI; @@ -29,14 +28,22 @@ public static class ValidationBindingMixins /// An instance of that, when disposed, /// disconnects the binding. /// - public static IReactiveBinding BindWithValidation(this TView view, TViewModel viewModel, Expression> viewModelPropertySelector, Expression> frameworkElementSelector) - where TView : class, IViewFor + public static IReactiveBinding BindWithValidation( + this TView view, + TViewModel viewModel, + Expression> viewModelPropertySelector, + Expression> frameworkElementSelector) where TViewModel : class + where TView : class, IViewFor { ArgumentExceptionHelper.ThrowIfNull(viewModelPropertySelector); ArgumentExceptionHelper.ThrowIfNull(frameworkElementSelector); - return new ValidationBindingWpf(view, viewModel, viewModelPropertySelector, frameworkElementSelector); + return new ValidationBindingWpf( + view, + viewModel, + viewModelPropertySelector, + frameworkElementSelector); } } diff --git a/src/ReactiveUI.Wpf/Binding/ValidationBindingWpf.cs b/src/ReactiveUI.Wpf/Binding/ValidationBindingWpf.cs index d9674fdaa0..d8c67a2b17 100644 --- a/src/ReactiveUI.Wpf/Binding/ValidationBindingWpf.cs +++ b/src/ReactiveUI.Wpf/Binding/ValidationBindingWpf.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -10,8 +10,6 @@ using System.Windows.Markup.Primitives; using System.Windows.Media; -using DynamicData; - namespace ReactiveUI.Wpf.Binding; /// @@ -27,12 +25,43 @@ internal class ValidationBindingWpf : IReact where TView : class, IViewFor where TViewModel : class { + /// + /// The WPF control that the binding targets. + /// private readonly FrameworkElement _control; + + /// + /// The dependency property on the control that is bound. + /// private readonly DependencyProperty _dpPropertyName; + + /// + /// The view model that supplies the bound value. + /// private readonly TViewModel _viewModel; + + /// + /// The dotted property path on the view model that is bound. + /// private readonly string _vmPropertyName; + + /// + /// The disposable that tears down the active binding. + /// private IDisposable? _inner; + /// + /// Tracks whether this instance has already been disposed. + /// + private bool _disposed; + + /// + /// Initializes a new instance of the class. + /// + /// The view that owns the binding. + /// The view model that supplies the bound value. + /// An expression selecting the view model property to bind. + /// An expression selecting the view property to bind. public ValidationBindingWpf( TView view, TViewModel viewModel, @@ -51,11 +80,15 @@ public ValidationBindingWpf( var controlName = ExtractControlName(viewExpressionChain, typeof(TView)); _control = FindControlByName(view as DependencyObject, controlName) - ?? throw new ArgumentException($"Control '{controlName}' not found in view {typeof(TView).Name}", nameof(viewProperty)); + ?? throw new ArgumentException( + $"Control '{controlName}' not found in view {typeof(TView).Name}", + nameof(viewProperty)); var propertyName = viewExpressionChain.LastOrDefault()?.GetMemberInfo()?.Name; _dpPropertyName = GetDependencyProperty(_control, propertyName) - ?? throw new ArgumentException($"Dependency property '{propertyName}' not found on {typeof(TVProp).Name}", nameof(viewProperty)); + ?? throw new ArgumentException( + $"Dependency property '{propertyName}' not found on {typeof(TVProp).Name}", + nameof(viewProperty)); Changed = Reflection.ViewModelWhenAnyValue(viewModel, view, ViewModelExpression) .Select(static tvm => (TVMProp?)tvm) @@ -96,13 +129,15 @@ public ValidationBindingWpf( /// A disposable that can be used to remove the binding. public IDisposable Bind() { - _control.SetBinding(_dpPropertyName, new System.Windows.Data.Binding - { - Source = _viewModel, - Path = new(_vmPropertyName), - Mode = BindingMode.TwoWay, - UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, - }); + _control.SetBinding( + _dpPropertyName, + new System.Windows.Data.Binding + { + Source = _viewModel, + Path = new(_vmPropertyName), + Mode = BindingMode.TwoWay, + UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged + }); _inner = Disposable.Create(() => BindingOperations.ClearBinding(_control, _dpPropertyName)); @@ -114,7 +149,7 @@ public IDisposable Bind() /// public void Dispose() { - _inner?.Dispose(); + Dispose(true); GC.SuppressFinalize(this); } @@ -146,14 +181,17 @@ internal static string ExtractControlName(System.Linq.Expressions.Expression[] e { if (expressionChain.Length < 2) { - throw new ArgumentException($"Expression chain too short to contain a control reference on {viewType.Name}", nameof(expressionChain)); + throw new ArgumentException( + $"Expression chain too short to contain a control reference on {viewType.Name}", + nameof(expressionChain)); } var lastIndex = expressionChain.Length - 1; var controlExpression = expressionChain[lastIndex - 1]; var controlName = controlExpression.GetMemberInfo()?.Name; - return controlName ?? throw new ArgumentException($"Control name not found on {viewType.Name}", nameof(expressionChain)); + return controlName ?? + throw new ArgumentException($"Control name not found on {viewType.Name}", nameof(expressionChain)); } /// @@ -261,6 +299,25 @@ internal static IEnumerable EnumerateAttachedProperties(obje return FindControlsByNameIterator(parent, name).FirstOrDefault(); } + /// + /// Releases the resources used by the binding. + /// + /// True to release managed resources; otherwise, false. + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + _inner?.Dispose(); + } + + _disposed = true; + } + /// /// Recursively searches the visual tree for controls matching the specified name. /// diff --git a/src/ReactiveUI.Wpf/Builder/WpfReactiveUIBuilderExtensions.cs b/src/ReactiveUI.Wpf/Builder/WpfReactiveUIBuilderExtensions.cs index d66d149125..7de391fe3f 100644 --- a/src/ReactiveUI.Wpf/Builder/WpfReactiveUIBuilderExtensions.cs +++ b/src/ReactiveUI.Wpf/Builder/WpfReactiveUIBuilderExtensions.cs @@ -1,10 +1,8 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Windows; - using Splat.Builder; namespace ReactiveUI.Builder; @@ -20,13 +18,8 @@ public static class WpfReactiveUIBuilderExtensions /// /// The WPF main thread scheduler. /// - public static IScheduler WpfMainThreadScheduler { get; } = new WaitForDispatcherScheduler( - static () => - { - var dispatcher = Application.Current?.Dispatcher - ?? throw new InvalidOperationException("WPF Application has not been initialized yet."); - return new DispatcherScheduler(dispatcher); - }); + public static IScheduler WpfMainThreadScheduler { get; } = + new WaitForDispatcherScheduler(static () => DispatcherScheduler.Current); /// /// Configures ReactiveUI for WPF platform with appropriate schedulers. diff --git a/src/ReactiveUI.Wpf/Common/AutoDataTemplateBindingHook.cs b/src/ReactiveUI.Wpf/Common/AutoDataTemplateBindingHook.cs index adf5d53010..a1339cca07 100644 --- a/src/ReactiveUI.Wpf/Common/AutoDataTemplateBindingHook.cs +++ b/src/ReactiveUI.Wpf/Common/AutoDataTemplateBindingHook.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -21,19 +21,24 @@ public class AutoDataTemplateBindingHook : IPropertyBindingHook /// public static Lazy DefaultItemTemplate { get; } = new(static () => { - const string template = " " + - "" + - ""; + const string Template = " " + + "" + + ""; var assemblyName = typeof(AutoDataTemplateBindingHook).Assembly.FullName; - assemblyName = assemblyName?.Substring(0, assemblyName.IndexOf(',')); + assemblyName = assemblyName?.Substring(0, assemblyName.IndexOf(",", StringComparison.Ordinal)); - return (DataTemplate)XamlReader.Parse(template.Replace("__ASSEMBLYNAME__", assemblyName)); + return (DataTemplate)XamlReader.Parse(Template.Replace("__ASSEMBLYNAME__", assemblyName, StringComparison.Ordinal)); }); /// - public bool ExecuteHook(object? source, object target, Func[]> getCurrentViewModelProperties, Func[]> getCurrentViewProperties, BindingDirection direction) + public bool ExecuteHook( + object? source, + object target, + Func[]> getCurrentViewModelProperties, + Func[]> getCurrentViewProperties, + BindingDirection direction) { ArgumentExceptionHelper.ThrowIfNull(getCurrentViewProperties); @@ -50,7 +55,7 @@ public bool ExecuteHook(object? source, object target, Func [Flags] +[SuppressMessage( + "Minor Code Smell", + "S2342:Enumeration types should comply with a naming convention", + Justification = "Established public API; renaming is breaking.")] public enum BooleanToVisibilityHint { /// diff --git a/src/ReactiveUI.Wpf/Common/BooleanToVisibilityTypeConverter.cs b/src/ReactiveUI.Wpf/Common/BooleanToVisibilityTypeConverter.cs index 3bfa3dd71c..fa71689fa0 100644 --- a/src/ReactiveUI.Wpf/Common/BooleanToVisibilityTypeConverter.cs +++ b/src/ReactiveUI.Wpf/Common/BooleanToVisibilityTypeConverter.cs @@ -1,13 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - #if HAS_MAUI using Microsoft.Maui; - #endif #if HAS_WINUI using Microsoft.UI.Xaml; @@ -42,10 +39,10 @@ namespace ReactiveUI; public sealed class BooleanToVisibilityTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(bool from, object? conversionHint, [NotNullWhen(true)] out Visibility result) + public override bool TryConvert(bool from, object? conversionHint, out Visibility result) { var hint = conversionHint is BooleanToVisibilityHint visibilityHint ? visibilityHint diff --git a/src/ReactiveUI.Wpf/Common/PlatformOperations.cs b/src/ReactiveUI.Wpf/Common/PlatformOperations.cs index 491ff21bc4..f651a7c127 100644 --- a/src/ReactiveUI.Wpf/Common/PlatformOperations.cs +++ b/src/ReactiveUI.Wpf/Common/PlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI.Wpf/Common/ReactivePage.cs b/src/ReactiveUI.Wpf/Common/ReactivePage.cs index 6a072b13b7..aac01b4525 100644 --- a/src/ReactiveUI.Wpf/Common/ReactivePage.cs +++ b/src/ReactiveUI.Wpf/Common/ReactivePage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -94,8 +94,8 @@ namespace ReactiveUI; partial #endif class ReactivePage : - Page, IViewFor - where TViewModel : class + Page, IViewFor + where TViewModel : class { #if !HAS_MAUI /// @@ -106,7 +106,7 @@ class ReactivePage : "ViewModel", typeof(TViewModel), typeof(ReactivePage), - new PropertyMetadata(null)); + new(null)); #else /// /// The view model bindable property. @@ -164,11 +164,11 @@ protected ReactivePage(IntPtr handle) /// /// Initializes a new instance of the class. /// - public ReactivePage() => this.WhenActivated(disposables => - { - // No-op, but ensures that when the Page is activated, - // any IActivatableViewModel logic in the ViewModel is also triggered. - }); + public ReactivePage() => this.WhenActivated((CompositeDisposable _) => + { + // No-op, but ensures that when the Page is activated, + // any IActivatableViewModel logic in the ViewModel is also triggered. + }); #endif /// @@ -198,6 +198,7 @@ protected override void OnBindingContextChanged() ViewModel = BindingContext as TViewModel; } - private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext = newValue; + private static void OnViewModelChanged(BindableObject bindableObject, object oldValue, object newValue) => bindableObject.BindingContext + = newValue; #endif } diff --git a/src/ReactiveUI.Wpf/Common/ReactiveUserControl.cs b/src/ReactiveUI.Wpf/Common/ReactiveUserControl.cs index 265cffbae6..9223e9bf13 100644 --- a/src/ReactiveUI.Wpf/Common/ReactiveUserControl.cs +++ b/src/ReactiveUI.Wpf/Common/ReactiveUserControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -90,9 +90,9 @@ namespace ReactiveUI; #if HAS_UNO partial #endif -class ReactiveUserControl : - UserControl, IViewFor - where TViewModel : class + class ReactiveUserControl : + UserControl, IViewFor + where TViewModel : class { /// /// The view model dependency property. @@ -102,7 +102,7 @@ class ReactiveUserControl : "ViewModel", typeof(TViewModel), typeof(ReactiveUserControl), - new PropertyMetadata(null)); + new(null)); #if HAS_UNO /// @@ -148,11 +148,11 @@ protected ReactiveUserControl(IntPtr handle) /// /// Initializes a new instance of the class. /// - public ReactiveUserControl() => this.WhenActivated(disposables => - { - // No-op, but ensures that when the Page is activated, - // any IActivatableViewModel logic in the ViewModel is also triggered. - }); + public ReactiveUserControl() => this.WhenActivated((CompositeDisposable _) => + { + // No-op, but ensures that when the Page is activated, + // any IActivatableViewModel logic in the ViewModel is also triggered. + }); #endif /// diff --git a/src/ReactiveUI.Wpf/Common/RoutedViewHost.cs b/src/ReactiveUI.Wpf/Common/RoutedViewHost.cs index 05d945afa3..844f59c707 100644 --- a/src/ReactiveUI.Wpf/Common/RoutedViewHost.cs +++ b/src/ReactiveUI.Wpf/Common/RoutedViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,7 +13,6 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; #else - using System.Windows; #endif @@ -40,20 +39,27 @@ class RoutedViewHost : TransitioningContentControl, IActivatableView, IEnableLog /// The router dependency property. /// public static readonly DependencyProperty RouterProperty = - DependencyProperty.Register("Router", typeof(RoutingState), typeof(RoutedViewHost), new PropertyMetadata(null)); + DependencyProperty.Register("Router", typeof(RoutingState), typeof(RoutedViewHost), new(null)); /// /// The default content property. /// public static readonly DependencyProperty DefaultContentProperty = - DependencyProperty.Register("DefaultContent", typeof(object), typeof(RoutedViewHost), new PropertyMetadata(null)); + DependencyProperty.Register("DefaultContent", typeof(object), typeof(RoutedViewHost), new(null)); /// /// The view contract observable property. /// public static readonly DependencyProperty ViewContractObservableProperty = - DependencyProperty.Register("ViewContractObservable", typeof(IObservable), typeof(RoutedViewHost), new PropertyMetadata(Observable.Default)); + DependencyProperty.Register( + "ViewContractObservable", + typeof(IObservable), + typeof(RoutedViewHost), + new(Observable.Default)); + /// + /// Stores the most recently observed view contract. + /// private string? _viewContract; /// @@ -65,13 +71,16 @@ public RoutedViewHost() VerticalContentAlignment = VerticalAlignment.Stretch; var platform = AppLocator.Current.GetService(); - Func platformGetter = () => default; + Func platformGetter = () => null; if (platform is null) { // NB: This used to be an error but WPF design mode can't read // good or do other stuff good. - this.Log().Error("Couldn't find an IPlatformOperations implementation. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance."); + this.Log().Error( + "Couldn't find an IPlatformOperations implementation. Please make sure you have installed " + + "the latest version of the ReactiveUI packages for your platform. " + + "See https://reactiveui.net/docs/getting-started/installation for guidance."); } else { @@ -81,20 +90,22 @@ public RoutedViewHost() ViewContractObservable = ModeDetector.InUnitTestRunner() ? Observable.Never : Observable.FromEvent( - eventHandler => - { - void Handler(object sender, SizeChangedEventArgs e) => eventHandler(platformGetter()); - return Handler; - }, - x => SizeChanged += x, - x => SizeChanged -= x) - .StartWith(platformGetter()) - .DistinctUntilChanged(); + eventHandler => + { + void Handler(object sender, SizeChangedEventArgs e) => eventHandler(platformGetter()); + return Handler; + }, + x => SizeChanged += x, + x => SizeChanged -= x) + .StartWith(platformGetter()) + .DistinctUntilChanged(); IRoutableViewModel? currentViewModel = null; - var vmAndContract = this.WhenAnyObservable(x => x.Router.CurrentViewModel).Do(x => currentViewModel = x).StartWith(currentViewModel).CombineLatest( - this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x).StartWith(ViewContract), - (viewModel, contract) => (viewModel, contract)); + var vmAndContract = this.WhenAnyObservable(x => x.Router.CurrentViewModel).Do(x => currentViewModel = x) + .StartWith(currentViewModel).CombineLatest( + this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x) + .StartWith(ViewContract), + (viewModel, contract) => (viewModel, contract)); // NB: The DistinctUntilChanged is useful because most views in // WinRT will end up getting here twice - once for configuring @@ -139,6 +150,10 @@ public IObservable ViewContractObservable /// /// Gets or sets the view contract. /// + [SuppressMessage( + "Critical Bug", + "S4275:Getters and setters should access the expected fields", + Justification = "Setter intentionally routes through ViewContractObservable rather than the field.")] public string? ViewContract { get => _viewContract; @@ -153,6 +168,10 @@ public string? ViewContract /// public IViewLocator? ViewLocator { get; set; } + /// + /// Resolves and displays the view for the supplied view model and contract. + /// + /// The view model and contract to resolve a view for. private void ResolveViewForViewModel((IRoutableViewModel? viewModel, string? contract) x) { if (x.viewModel is null) @@ -162,7 +181,8 @@ private void ResolveViewForViewModel((IRoutableViewModel? viewModel, string? con } var viewLocator = ViewLocator ?? ReactiveUI.ViewLocator.Current; - var view = (viewLocator.ResolveView(x.viewModel, x.contract) ?? viewLocator.ResolveView(x.viewModel)) ?? throw new Exception($"Couldn't find view for '{x.viewModel}'."); + var view = (viewLocator.ResolveView(x.viewModel, x.contract) ?? viewLocator.ResolveView(x.viewModel)) ?? + throw new InvalidOperationException($"Couldn't find view for '{x.viewModel}'."); view.ViewModel = x.viewModel; Content = view; } diff --git a/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs b/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs index e7c866c02b..c6a4e31448 100644 --- a/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs +++ b/src/ReactiveUI.Wpf/Common/ViewModelViewHost.cs @@ -1,15 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if HAS_WINUI - using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; #elif HAS_UNO - using System.Windows; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -21,7 +19,6 @@ #endif #if HAS_UNO - namespace ReactiveUI.Uno #else @@ -43,26 +40,33 @@ class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger /// The default content dependency property. /// public static readonly DependencyProperty DefaultContentProperty = - DependencyProperty.Register(nameof(DefaultContent), typeof(object), typeof(ViewModelViewHost), new PropertyMetadata(null)); + DependencyProperty.Register(nameof(DefaultContent), typeof(object), typeof(ViewModelViewHost), new(null)); /// /// The view model dependency property. /// public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register(nameof(ViewModel), typeof(object), typeof(ViewModelViewHost), new PropertyMetadata(null)); + DependencyProperty.Register(nameof(ViewModel), typeof(object), typeof(ViewModelViewHost), new(null)); /// /// The view contract observable dependency property. /// public static readonly DependencyProperty ViewContractObservableProperty = - DependencyProperty.Register(nameof(ViewContractObservable), typeof(IObservable), typeof(ViewModelViewHost), new PropertyMetadata(Observable.Default)); + DependencyProperty.Register( + nameof(ViewContractObservable), + typeof(IObservable), + typeof(ViewModelViewHost), + new(Observable.Default)); /// /// The ContractFallbackByPass dependency property. /// public static readonly DependencyProperty ContractFallbackByPassProperty = - DependencyProperty.Register("ContractFallbackByPass", typeof(bool), typeof(ViewModelViewHost), new PropertyMetadata(false)); + DependencyProperty.Register("ContractFallbackByPass", typeof(bool), typeof(ViewModelViewHost), new(false)); + /// + /// Stores the most recently observed view contract. + /// private string? _viewContract; /// @@ -71,33 +75,37 @@ class ViewModelViewHost : TransitioningContentControl, IViewFor, IEnableLogger public ViewModelViewHost() { var platform = AppLocator.Current.GetService(); - Func platformGetter = () => default; + Func platformGetter = () => null; if (platform is null) { // NB: This used to be an error but WPF design mode can't read // good or do other stuff good. - this.Log().Error("Couldn't find an IPlatformOperations implementation. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance."); + this.Log().Error( + "Couldn't find an IPlatformOperations implementation. Please make sure you have installed " + + "the latest version of the ReactiveUI packages for your platform. " + + "See https://reactiveui.net/docs/getting-started/installation for guidance."); } else { - platformGetter = () => platform.GetOrientation(); + platformGetter = platform.GetOrientation; } ViewContractObservable = ModeDetector.InUnitTestRunner() ? Observable.Never : Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, SizeChangedEventArgs e) => eventHandler(platformGetter()); - return Handler; - }, - x => SizeChanged += x, - x => SizeChanged -= x) - .StartWith(platformGetter()) - .DistinctUntilChanged(); - - var contractChanged = this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x).StartWith(ViewContract); + eventHandler => + { + void Handler(object? sender, SizeChangedEventArgs e) => eventHandler(platformGetter()); + return Handler; + }, + x => SizeChanged += x, + x => SizeChanged -= x) + .StartWith(platformGetter()) + .DistinctUntilChanged(); + + var contractChanged = this.WhenAnyObservable(x => x.ViewContractObservable).Do(x => _viewContract = x) + .StartWith(ViewContract); var viewModelChanged = this.WhenAnyValue(nameof(ViewModel)).StartWith(ViewModel); var vmAndContract = contractChanged .CombineLatest(viewModelChanged, (contract, vm) => (ViewModel: vm, Contract: contract)); @@ -105,8 +113,8 @@ public ViewModelViewHost() this.WhenActivated(d => { d(contractChanged - .ObserveOn(RxSchedulers.MainThreadScheduler) - .Subscribe(x => _viewContract = x ?? string.Empty)); + .ObserveOn(RxSchedulers.MainThreadScheduler) + .Subscribe(x => _viewContract = x ?? string.Empty)); d(vmAndContract.DistinctUntilChanged().Subscribe(x => ResolveViewForViewModel(x.ViewModel, x.Contract))); }); @@ -142,6 +150,10 @@ public object? ViewModel /// /// Gets or sets the view contract. /// + [SuppressMessage( + "Critical Bug", + "S4275:Getters and setters should access the expected fields", + Justification = "Setter intentionally routes through ViewContractObservable rather than the field.")] public string? ViewContract { get => _viewContract; @@ -186,7 +198,8 @@ protected virtual void ResolveViewForViewModel(object? viewModel, string? contra if (viewInstance is null) { Content = DefaultContent; - this.Log().Warn($"The {nameof(ViewModelViewHost)} could not find a valid view for the view model of type {viewModel.GetType()} and value {viewModel}."); + this.Log().Warn( + $"The {nameof(ViewModelViewHost)} could not find a valid view for the view model of type {viewModel.GetType()} and value {viewModel}."); return; } diff --git a/src/ReactiveUI.Wpf/Common/VisibilityToBooleanTypeConverter.cs b/src/ReactiveUI.Wpf/Common/VisibilityToBooleanTypeConverter.cs index bbf06e8320..ea7f45db0d 100644 --- a/src/ReactiveUI.Wpf/Common/VisibilityToBooleanTypeConverter.cs +++ b/src/ReactiveUI.Wpf/Common/VisibilityToBooleanTypeConverter.cs @@ -1,13 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; - #if HAS_MAUI using Microsoft.Maui; - #endif #if HAS_WINUI using Microsoft.UI.Xaml; @@ -41,10 +38,10 @@ namespace ReactiveUI; public sealed class VisibilityToBooleanTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(Visibility from, object? conversionHint, [NotNullWhen(true)] out bool result) + public override bool TryConvert(Visibility from, object? conversionHint, out bool result) { var hint = conversionHint is BooleanToVisibilityHint visibilityHint ? visibilityHint diff --git a/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs b/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs index a68cbb36c0..cb84d94883 100644 --- a/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs +++ b/src/ReactiveUI.Wpf/DependencyObjectObservableForProperty.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,27 +13,54 @@ namespace ReactiveUI; /// public class DependencyObjectObservableForProperty : ICreatesObservableForProperty { + /// The affinity returned when the type exposes a matching dependency property. + private const int DependencyPropertyAffinity = 4; + + /// + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + /// - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged) { - if (!typeof(DependencyObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + if (type is null || !typeof(DependencyObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { return 0; } - return GetDependencyProperty(type, propertyName) is not null ? 4 : 0; + return GetDependencyProperty(type, propertyName) is not null ? DependencyPropertyAffinity : 0; } /// - public IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) + public IObservable> GetNotificationForProperty( + object sender, + System.Linq.Expressions.Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + public IObservable> GetNotificationForProperty( + object sender, + System.Linq.Expressions.Expression expression, + string propertyName, + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + + /// + public IObservable> GetNotificationForProperty( + object sender, + System.Linq.Expressions.Expression expression, + string propertyName, + bool beforeChanged, + bool suppressWarnings) { ArgumentExceptionHelper.ThrowIfNull(sender); var type = sender.GetType(); var dependencyProperty = GetDependencyProperty(type, propertyName) ?? throw new ArgumentException( - $"The property {propertyName} does not have a dependency property.", - nameof(propertyName)); + $"The property {propertyName} does not have a dependency property.", + nameof(propertyName)); var dependencyPropertyDescriptor = DependencyPropertyDescriptor.FromProperty(dependencyProperty, type); if (dependencyPropertyDescriptor is null) @@ -43,22 +70,30 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang this.Log().Error("Couldn't find dependency property " + propertyName + " on " + type.Name); } - throw new NullReferenceException("Couldn't find dependency property " + propertyName + " on " + type.Name); + throw new InvalidOperationException("Couldn't find dependency property " + propertyName + " on " + type.Name); } return Observable.Create>(subj => { - var handler = new EventHandler((_, _) => subj.OnNext(new ObservedChange(sender, expression, default))); + var handler = new EventHandler((_, _) => + subj.OnNext(new ObservedChange(sender, expression, null))); var scheduler = RxSchedulers.MainThreadScheduler; dependencyPropertyDescriptor.AddValueChanged(sender, handler); - return Disposable.Create(() => scheduler.Schedule(() => dependencyPropertyDescriptor.RemoveValueChanged(sender, handler))); + return Disposable.Create(() => + scheduler.Schedule(() => dependencyPropertyDescriptor.RemoveValueChanged(sender, handler))); }); } + /// Gets the dependency property for the named property on the supplied type, if any. + /// The type to inspect. + /// The property name to resolve. + /// The matching dependency property, or null when none exists. private static DependencyProperty? GetDependencyProperty(Type type, string propertyName) { - var fi = Array.Find(type.GetTypeInfo().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public), x => x.Name == propertyName + "Property" && x.IsStatic); + var fi = Array.Find( + type.GetTypeInfo().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public), + x => x.Name == propertyName + "Property" && x.IsStatic); return (DependencyProperty?)fi?.GetValue(null); } diff --git a/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj b/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj index 2104ea4586..43a7148515 100644 --- a/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj +++ b/src/ReactiveUI.Wpf/ReactiveUI.Wpf.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIWindowsOnlyTargets) Contains the ReactiveUI platform specific extensions for Windows Presentation Foundation (WPF) @@ -10,38 +10,37 @@ false - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + + - + - + - - + + diff --git a/src/ReactiveUI.Wpf/ReactiveWindow.cs b/src/ReactiveUI.Wpf/ReactiveWindow.cs index 49a5fafabf..30effb2304 100644 --- a/src/ReactiveUI.Wpf/ReactiveWindow.cs +++ b/src/ReactiveUI.Wpf/ReactiveWindow.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -48,10 +48,10 @@ public class ReactiveWindow : /// public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register( - "ViewModel", - typeof(TViewModel), - typeof(ReactiveWindow), - new PropertyMetadata(null)); + "ViewModel", + typeof(TViewModel), + typeof(ReactiveWindow), + new(null)); /// /// Initializes a new instance of the class. @@ -59,11 +59,11 @@ public class ReactiveWindow : /// When the window is activated, this constructor ensures that the ViewModel's activation logic /// is also triggered if the ViewModel implements IActivatableViewModel. This enables coordinated activation and /// deactivation of resources tied to the window and its ViewModel. - public ReactiveWindow() => this.WhenActivated(disposables => - { - // No-op, but ensures that when the Page is activated, - // any IActivatableViewModel logic in the ViewModel is also triggered. - }); + public ReactiveWindow() => this.WhenActivated((CompositeDisposable _) => + { + // No-op, but ensures that when the Page is activated, + // any IActivatableViewModel logic in the ViewModel is also triggered. + }); /// /// Gets the binding root view model. diff --git a/src/ReactiveUI.Wpf/Registrations.cs b/src/ReactiveUI.Wpf/Registrations.cs index 93ce340ed1..13f4036516 100644 --- a/src/ReactiveUI.Wpf/Registrations.cs +++ b/src/ReactiveUI.Wpf/Registrations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,11 +17,15 @@ public void Register(IRegistrar registrar) registrar.RegisterConstant(static () => new PlatformOperations()); registrar.RegisterConstant(static () => new ActivationForViewFetcher()); - registrar.RegisterConstant(static () => new DependencyObjectObservableForProperty()); + registrar.RegisterConstant(static () => + new DependencyObjectObservableForProperty()); + + // WPF property binder that resolves inherited DependencyProperty metadata (see #4350). registrar.RegisterConstant(static () => new WpfPropertyBinderImplementation()); // WPF-specific command rebinding optimization - registrar.RegisterConstant(static () => new WpfCommandRebindingCustomizer()); + registrar.RegisterConstant(static () => + new WpfCommandRebindingCustomizer()); // WPF-specific converters registrar.RegisterConstant(static () => new BooleanToVisibilityTypeConverter()); diff --git a/src/ReactiveUI.Wpf/Rx/Concurrency/ControlScheduler.cs b/src/ReactiveUI.Wpf/Rx/Concurrency/ControlScheduler.cs index b556d720fc..95e7e4ffb7 100644 --- a/src/ReactiveUI.Wpf/Rx/Concurrency/ControlScheduler.cs +++ b/src/ReactiveUI.Wpf/Rx/Concurrency/ControlScheduler.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // diff --git a/src/ReactiveUI.Wpf/Rx/Concurrency/DispatcherScheduler.cs b/src/ReactiveUI.Wpf/Rx/Concurrency/DispatcherScheduler.cs index 0b44209b38..f6940011f4 100644 --- a/src/ReactiveUI.Wpf/Rx/Concurrency/DispatcherScheduler.cs +++ b/src/ReactiveUI.Wpf/Rx/Concurrency/DispatcherScheduler.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // diff --git a/src/ReactiveUI.Wpf/Rx/Internal/Constants.cs b/src/ReactiveUI.Wpf/Rx/Internal/Constants.cs index 99f1f15d07..168c419264 100644 --- a/src/ReactiveUI.Wpf/Rx/Internal/Constants.cs +++ b/src/ReactiveUI.Wpf/Rx/Internal/Constants.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // diff --git a/src/ReactiveUI.Wpf/Rx/Linq/ControlObservable.cs b/src/ReactiveUI.Wpf/Rx/Linq/ControlObservable.cs index a46fe49a58..f2512ee7c4 100644 --- a/src/ReactiveUI.Wpf/Rx/Linq/ControlObservable.cs +++ b/src/ReactiveUI.Wpf/Rx/Linq/ControlObservable.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // diff --git a/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs b/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs index 19787135a7..5c21fa6dff 100644 --- a/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs +++ b/src/ReactiveUI.Wpf/Rx/Linq/DispatcherObservable.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // diff --git a/src/ReactiveUI.Wpf/Rx/Linq/Observable.Remoting.cs b/src/ReactiveUI.Wpf/Rx/Linq/Observable.Remoting.cs index 49539e528f..82ec9ee5ce 100644 --- a/src/ReactiveUI.Wpf/Rx/Linq/Observable.Remoting.cs +++ b/src/ReactiveUI.Wpf/Rx/Linq/Observable.Remoting.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // @@ -24,7 +24,8 @@ public static partial class RemotingObservable /// Source sequence. /// The observable sequence that supports remote subscriptions. /// is null. - [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId + = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] public static IObservable Remotable(this IObservable source) { if (source == null) @@ -43,7 +44,8 @@ public static IObservable Remotable(this IObservable /// Lease object to control lifetime of the remotable sequence. Notice null is a supported value. /// The observable sequence that supports remote subscriptions. /// is null. - [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId + = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] public static IObservable Remotable(this IObservable source, ILease lease) { if (source == null) @@ -61,7 +63,8 @@ public static IObservable Remotable(this IObservable /// Source sequence. /// The observable sequence that supports remote subscriptions. /// is null. - [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId + = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] public static IQbservable Remotable(this IQbservable source) { if (source == null) @@ -90,7 +93,8 @@ public static IQbservable Remotable(this IQbservable /// Lease object to control lifetime of the remotable sequence. Notice null is a supported value. /// The observable sequence that supports remote subscriptions. /// is null. - [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId + = "Remotable", Justification = "In honor of the .NET Remoting heroes.")] public static IQbservable Remotable(this IQbservable source, ILease lease) { if (source == null) diff --git a/src/ReactiveUI.Wpf/Rx/Linq/QueryLanguage.Remoting.cs b/src/ReactiveUI.Wpf/Rx/Linq/QueryLanguage.Remoting.cs index 9be9d6f105..903aa7e207 100644 --- a/src/ReactiveUI.Wpf/Rx/Linq/QueryLanguage.Remoting.cs +++ b/src/ReactiveUI.Wpf/Rx/Linq/QueryLanguage.Remoting.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. // @@ -55,8 +55,13 @@ // // The two CodeAnalysis suppressions below are explained by the Justification property (scroll to the right): // -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObserver`1.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObservable`1+RemotableSubscription.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope + = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObserver`1.#Unregister()", Justification = + "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope + = "member", Target = + "System.Reactive.Linq.QueryLanguage+RemotableObservable`1+RemotableSubscription.#Unregister()", Justification = + "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")] namespace System.Reactive.Linq { diff --git a/src/ReactiveUI.Wpf/Themes/Generic.xaml b/src/ReactiveUI.Wpf/Themes/Generic.xaml index 2f05961083..0c32dfef70 100644 --- a/src/ReactiveUI.Wpf/Themes/Generic.xaml +++ b/src/ReactiveUI.Wpf/Themes/Generic.xaml @@ -354,7 +354,9 @@ - + @@ -364,12 +366,15 @@ - + - + Collapsed @@ -389,7 +394,9 @@ To="-90" Duration="00:00:00.2" /> - + Collapsed @@ -401,7 +408,9 @@ - + @@ -411,12 +420,15 @@ - + - + Collapsed @@ -436,7 +448,9 @@ To="-90" Duration="00:00:00.2" /> - + Collapsed @@ -448,7 +462,9 @@ - + @@ -458,12 +474,15 @@ - + - + Collapsed @@ -483,7 +502,9 @@ To="-90" Duration="00:00:00.2" /> - + Collapsed @@ -495,7 +516,9 @@ - + @@ -505,12 +528,15 @@ - + - + Collapsed @@ -530,7 +556,9 @@ To="-90" Duration="00:00:00.2" /> - + Collapsed diff --git a/src/ReactiveUI.Wpf/TransitioningContentControl.cs b/src/ReactiveUI.Wpf/TransitioningContentControl.cs index 482dc8a166..2cf7479f53 100644 --- a/src/ReactiveUI.Wpf/TransitioningContentControl.cs +++ b/src/ReactiveUI.Wpf/TransitioningContentControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -8,7 +8,6 @@ using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; - using ReactiveUI.Helpers; // This control is gratefully borrowed from http://blog.landdolphin.net/?p=17 @@ -152,10 +151,10 @@ public class TransitioningContentControl : ContentControl /// The default value is . /// public static readonly DependencyProperty TransitionProperty = DependencyProperty.RegisterAttached( - nameof(Transition), - typeof(TransitionType), - typeof(TransitioningContentControl), - new PropertyMetadata(TransitionType.Fade)); + nameof(Transition), + typeof(TransitionType), + typeof(TransitioningContentControl), + new(TransitionType.Fade)); /// /// Identifies the dependency property. @@ -164,10 +163,10 @@ public class TransitioningContentControl : ContentControl /// The default value is . /// public static readonly DependencyProperty TransitionDirectionProperty = DependencyProperty.RegisterAttached( - nameof(TransitionDirection), - typeof(TransitionDirection), - typeof(TransitioningContentControl), - new PropertyMetadata(TransitionDirection.Left)); + nameof(TransitionDirection), + typeof(TransitionDirection), + typeof(TransitioningContentControl), + new(TransitionDirection.Left)); /// /// Identifies the dependency property. @@ -176,15 +175,54 @@ public class TransitioningContentControl : ContentControl /// The default value is 0.3 seconds. /// public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.RegisterAttached( - nameof(Duration), - typeof(TimeSpan), - typeof(TransitioningContentControl), - new PropertyMetadata(TimeSpan.FromSeconds(0.3))); + nameof(Duration), + typeof(TimeSpan), + typeof(TransitioningContentControl), + new(TimeSpan.FromSeconds(DefaultDurationSeconds))); + /// + /// The name of the visual state group that contains the transition states. + /// private const string PresentationGroup = "PresentationStates"; + + /// + /// The name of the resting (non-transitioning) visual state. + /// private const string NormalState = "Normal"; + + /// + /// The default transition duration, in seconds. + /// + private const double DefaultDurationSeconds = 0.3; + + /// + /// The DPI scale used when DPI scaling is overridden for testing. + /// + private const double OverrideDpiScale = 1.25; + + /// + /// The minimum pixel dimension used when creating a render target bitmap. + /// + private const double MinimumPixelDimension = 1.0; + + /// + /// The number of states the bounce transition waits for before completing. + /// + private const int BounceTransitionStateCount = 2; + + /// + /// A value indicating whether a transition is currently in progress. + /// private bool _isTransitioning; + + /// + /// The storyboard used for the starting (outgoing) phase of a transition. + /// private Storyboard? _startingTransition; + + /// + /// The storyboard used for the completing (incoming) phase of a transition. + /// private Storyboard? _completingTransition; /// @@ -203,7 +241,14 @@ public class TransitioningContentControl : ContentControl /// The event is raised after the control returns to the Normal visual state and after the previous content /// snapshot has been released. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "RCS1159:Use EventHandler", Justification = "Using WPF's RoutedEventHandler pattern.")] + [SuppressMessage( + "Design", + "RCS1159:Use EventHandler", + Justification = "Using WPF's RoutedEventHandler pattern.")] + [SuppressMessage( + "Major Code Smell", + "S3908:Use EventHandler", + Justification = "Public WPF RoutedEventHandler event; matches the RCS1159 suppression.")] public event RoutedEventHandler? TransitionCompleted; /// @@ -213,7 +258,14 @@ public class TransitioningContentControl : ContentControl /// The event is raised when a transition is about to begin (after the control has prepared the outgoing snapshot and /// set the new content), but before the first visual state is entered. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "RCS1159:Use EventHandler", Justification = "Using WPF's RoutedEventHandler pattern.")] + [SuppressMessage( + "Design", + "RCS1159:Use EventHandler", + Justification = "Using WPF's RoutedEventHandler pattern.")] + [SuppressMessage( + "Major Code Smell", + "S3908:Use EventHandler", + Justification = "Public WPF RoutedEventHandler event; matches the RCS1159 suppression.")] public event RoutedEventHandler? TransitionStarted; /// @@ -315,7 +367,11 @@ public TransitionDirection Direction /// Gets or sets the transition duration. /// /// The duration. - public TimeSpan Duration { get => (TimeSpan)GetValue(TransitionDurationProperty); set => SetValue(TransitionDurationProperty, value); } + public TimeSpan Duration + { + get => (TimeSpan)GetValue(TransitionDurationProperty); + set => SetValue(TransitionDurationProperty, value); + } /// /// Gets or sets a value indicating whether to override DPI scaling for testing. @@ -331,10 +387,12 @@ internal Storyboard? StartingTransition set { _startingTransition = value; - if (_startingTransition is not null) + if (_startingTransition is null) { - SetTransitionDefaultValues(); + return; } + + SetTransitionDefaultValues(); } } @@ -354,11 +412,13 @@ internal Storyboard? CompletingTransition _completingTransition = value; - if (_completingTransition is not null) + if (_completingTransition is null) { - CompletingTransition!.Completed += OnTransitionCompleted; - SetTransitionDefaultValues(); + return; } + + CompletingTransition!.Completed += OnTransitionCompleted; + SetTransitionDefaultValues(); } } @@ -441,7 +501,7 @@ internal static DpiScale GetDpiScaleForElement(UIElement uiElement) if (OverrideDpi) { - dpiScale = new DpiScale(1.25, 1.25); + dpiScale = new(OverrideDpiScale, OverrideDpiScale); } return dpiScale; @@ -458,21 +518,21 @@ internal static DpiScale GetDpiScaleForElement(UIElement uiElement) /// internal static RenderTargetBitmap GetRenderTargetBitmapFromUiElement(UIElement uiElement) { - if (uiElement.RenderSize.Height == 0 || uiElement.RenderSize.Width == 0) + if (uiElement.RenderSize.Height <= 0 || uiElement.RenderSize.Width <= 0) { - return default!; + return null!; } var dpiScale = GetDpiScaleForElement(uiElement); - var pixelWidth = Math.Max(1.0, uiElement.RenderSize.Width * dpiScale.DpiScaleX); - var pixelHeight = Math.Max(1.0, uiElement.RenderSize.Height * dpiScale.DpiScaleY); + var pixelWidth = Math.Max(MinimumPixelDimension, uiElement.RenderSize.Width * dpiScale.DpiScaleX); + var pixelHeight = Math.Max(MinimumPixelDimension, uiElement.RenderSize.Height * dpiScale.DpiScaleY); var renderTargetBitmap = new RenderTargetBitmap( - Convert.ToInt32(pixelWidth), - Convert.ToInt32(pixelHeight), - dpiScale.PixelsPerInchX, - dpiScale.PixelsPerInchY, - PixelFormats.Pbgra32); + Convert.ToInt32(pixelWidth), + Convert.ToInt32(pixelHeight), + dpiScale.PixelsPerInchX, + dpiScale.PixelsPerInchY, + PixelFormats.Pbgra32); renderTargetBitmap.Render(uiElement); renderTargetBitmap.Freeze(); @@ -631,7 +691,8 @@ internal Storyboard GetTransitionStoryboardByName(string transitionName) if (PresentationStateGroup?.States is not IEnumerable states) { - throw new InvalidOperationException("Visual state group is not initialized or states collection is invalid."); + throw new InvalidOperationException( + "Visual state group is not initialized or states collection is invalid."); } var transition = states @@ -639,7 +700,8 @@ internal Storyboard GetTransitionStoryboardByName(string transitionName) .Select(o => o.Storyboard) .FirstOrDefault(); - return transition ?? throw new InvalidOperationException($"Transition '{transitionName}' not found in visual state group."); + return transition ?? + throw new InvalidOperationException($"Transition '{transitionName}' not found in visual state group."); } /// @@ -651,20 +713,28 @@ internal void SetTransitionDefaultValues() switch (Transition) { case TransitionType.Fade: - SetFadeTransitionDefaults(); - break; + { + SetFadeTransitionDefaults(); + break; + } case TransitionType.Slide: - SetSlideTransitionDefaults(); - break; + { + SetSlideTransitionDefaults(); + break; + } case TransitionType.Move: - SetMoveTransitionDefaults(); - break; + { + SetMoveTransitionDefaults(); + break; + } case TransitionType.Bounce: - SetBounceTransitionDefaults(); - break; + { + SetBounceTransitionDefaults(); + break; + } case TransitionType.Drop: break; @@ -699,17 +769,19 @@ private void AbortTransition() VisualStateManager.GoToState(this, NormalState, false); _isTransitioning = false; - if (PreviousImageSite is not null) + if (PreviousImageSite is null) { - if (PreviousImageSite.Source is RenderTargetBitmap renderTargetBitmap) - { - renderTargetBitmap.Clear(); - } + return; + } - // https://github.com/dotnet/wpf/issues/2397 - PreviousImageSite.Source = null; - PreviousImageSite.UpdateLayout(); + if (PreviousImageSite.Source is RenderTargetBitmap renderTargetBitmap) + { + renderTargetBitmap.Clear(); } + + // https://github.com/dotnet/wpf/issues/2397 + PreviousImageSite.Source = null; + PreviousImageSite.UpdateLayout(); } /// @@ -721,13 +793,13 @@ private void OnTransitionCompleted(object? sender, EventArgs e) { AbortTransition(); - TransitionCompleted?.Invoke(this, new RoutedEventArgs()); + TransitionCompleted?.Invoke(this, new()); } /// /// Raises the TransitionStarted event to signal that a transition has begun. /// - private void RaiseTransitionStarted() => TransitionStarted?.Invoke(this, new RoutedEventArgs()); + private void RaiseTransitionStarted() => TransitionStarted?.Invoke(this, new()); /// /// Queues a visual transition to display the specified content, applying the configured transition effect if @@ -760,7 +832,7 @@ private void QueueTransition(object newContent) if (Transition == TransitionType.Bounce) { (startingTransitionName, transitionInName) = ConfigureBounceTransition(); - statesRemaining = 2; + statesRemaining = BounceTransitionStateCount; StartingTransition!.Completed += NextState; } else @@ -784,11 +856,13 @@ private void QueueTransition(object newContent) void NextState(object? o, EventArgs e) { StartingTransition!.Completed -= NextState; - if (statesRemaining == 1) + if (statesRemaining != 1) { - statesRemaining--; - VisualStateManager.GoToState(this, transitionInName, false); + return; } + + statesRemaining--; + VisualStateManager.GoToState(this, transitionInName, false); } } } diff --git a/src/ReactiveUI.Wpf/WpfCommandRebindingCustomizer.cs b/src/ReactiveUI.Wpf/WpfCommandRebindingCustomizer.cs index 67d9351e28..ce004dd15d 100644 --- a/src/ReactiveUI.Wpf/WpfCommandRebindingCustomizer.cs +++ b/src/ReactiveUI.Wpf/WpfCommandRebindingCustomizer.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Windows.Input; -using System.Windows.Threading; namespace ReactiveUI.Wpf; @@ -38,23 +37,12 @@ public bool TryUpdateCommand(TControl? control, ICommand? command) var commandProperty = control.GetType().GetProperty("Command"); // If the control has a writable Command property, update it directly - if (commandProperty?.CanWrite == true) + if (commandProperty?.CanWrite != true) { - if (control is DispatcherObject dispatcherObject && !dispatcherObject.CheckAccess()) - { - dispatcherObject.Dispatcher.BeginInvoke( - new Action(() => commandProperty.SetValue(control, command)), - DispatcherPriority.Normal); - } - else - { - commandProperty.SetValue(control, command); - } - - return true; + return false; } - // Fall back to full rebind if Command property doesn't exist or isn't writable - return false; + commandProperty.SetValue(control, command); + return true; } } diff --git a/src/ReactiveUI/Activation/CanActivateViewFetcher.cs b/src/ReactiveUI/Activation/CanActivateViewFetcher.cs index fa2f6956f8..0c80252610 100644 --- a/src/ReactiveUI/Activation/CanActivateViewFetcher.cs +++ b/src/ReactiveUI/Activation/CanActivateViewFetcher.cs @@ -1,10 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; using System.Reflection; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -22,7 +25,7 @@ public class CanActivateViewFetcher : IActivationForViewFetcher /// The type of the view to evaluate for activation capability. Cannot be null. /// An integer affinity score: 10 if the view type implements ICanActivate; otherwise, 0. public int GetAffinityForView(Type view) => - typeof(ICanActivate).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0; + typeof(ICanActivate).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? BindingAffinity.ExactType : 0; /// /// Returns an observable sequence that indicates the activation state of the specified view. @@ -37,6 +40,140 @@ public int GetAffinityForView(Type view) => /// when it is deactivated. public IObservable GetActivationForView(IActivatableView view) => view is not ICanActivate canActivate - ? Observable.Return(false) - : canActivate.Activated.Select(static _ => true).Merge(canActivate.Deactivated.Select(static _ => false)); + ? SingleValueObservable.False + : new ActivationStateObservable(canActivate.Activated, canActivate.Deactivated); + + /// + /// A fused sink that emits on each tick and + /// on each tick — replacing + /// Activated.Select(_ => true).Merge(Deactivated.Select(_ => false)). + /// + /// Emits when the view is activated. + /// Emits when the view is deactivated. + private sealed class ActivationStateObservable(IObservable activated, IObservable deactivated) + : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer); + sink.Run(activated, deactivated); + return sink; + } + + /// Forwards a constant value for each tick of one source, completing only when both sources complete. + private sealed class Sink(IObserver downstream) : IDisposable + { + /// The number of sources that must complete before downstream completes. + private const int SourceCount = 2; + + /// Serializes notifications from the two sources and guards the completion count. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The activated-source subscription. + private readonly OnceDisposable _activated = new(); + + /// The deactivated-source subscription. + private readonly OnceDisposable _deactivated = new(); + + /// The number of sources that have completed; downstream completes when both have. + private int _completed; + + /// Whether this sink has been disposed or terminated. + private bool _done; + + /// Begins observing both sources. + /// Emits when the view is activated. + /// Emits when the view is deactivated. + public void Run(IObservable activated, IObservable deactivated) + { + _activated.Disposable = activated.Subscribe(new BranchObserver(this, true)); + _deactivated.Disposable = deactivated.Subscribe(new BranchObserver(this, false)); + } + + /// + public void Dispose() + { + lock (_gate) + { + if (_done) + { + return; + } + + _done = true; + } + + _activated.Dispose(); + _deactivated.Dispose(); + } + + /// Forwards this branch's constant value to the downstream observer. + /// The constant value for the branch that ticked. + private void OnBranchNext(bool value) + { + lock (_gate) + { + if (_done) + { + return; + } + + downstream.OnNext(value); + } + } + + /// Terminates with the error from either branch and disposes both subscriptions. + /// The error to forward. + private void OnBranchError(Exception error) + { + lock (_gate) + { + if (_done) + { + return; + } + + _done = true; + downstream.OnError(error); + } + + _activated.Dispose(); + _deactivated.Dispose(); + } + + /// Completes downstream once both branches have completed. + private void OnBranchCompleted() + { + lock (_gate) + { + if (_done || ++_completed < SourceCount) + { + return; + } + + _done = true; + downstream.OnCompleted(); + } + } + + /// Maps each tick of one source to a constant boolean and forwards it to the parent sink. + private sealed class BranchObserver(Sink parent, bool value) : IObserver + { + /// + public void OnNext(Unit unit) => parent.OnBranchNext(value); + + /// + public void OnError(Exception error) => parent.OnBranchError(error); + + /// + public void OnCompleted() => parent.OnBranchCompleted(); + } + } + } } diff --git a/src/ReactiveUI/Activation/IActivationForViewFetcher.cs b/src/ReactiveUI/Activation/IActivationForViewFetcher.cs index 8e100274c8..0f2303c9d2 100644 --- a/src/ReactiveUI/Activation/IActivationForViewFetcher.cs +++ b/src/ReactiveUI/Activation/IActivationForViewFetcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Activation/ViewForMixins.cs b/src/ReactiveUI/Activation/ViewForMixins.cs index facaf84167..d9da837bd3 100644 --- a/src/ReactiveUI/Activation/ViewForMixins.cs +++ b/src/ReactiveUI/Activation/ViewForMixins.cs @@ -1,9 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Globalization; +using System.Reactive.Disposables; + +using ReactiveUI.Internal; namespace ReactiveUI; @@ -21,16 +24,19 @@ namespace ReactiveUI; /// method; this should not be used in production code. public static class ViewForMixins { + /// + /// Cache mapping view types to their resolved activation fetcher, to avoid repeated service locator lookups. + /// private static readonly MemoizingMRUCache _activationFetcherCache = new( (t, _) => AppLocator.Current - .GetServices() - .Aggregate((count: 0, viewFetcher: default(IActivationForViewFetcher?)), (acc, x) => - { - var score = x?.GetAffinityForView(t) ?? 0; - return score > acc.count ? (score, x) : acc; - }).viewFetcher, + .GetServices() + .Aggregate((count: 0, viewFetcher: default(IActivationForViewFetcher?)), (acc, x) => + { + var score = x?.GetAffinityForView(t) ?? 0; + return score > acc.count ? (score, x) : acc; + }).viewFetcher, RxCacheSize.SmallCacheLimit); /// @@ -42,7 +48,8 @@ public static class ViewForMixins /// deactivated. /// The view model whose activation lifecycle will manage the disposables. Cannot be null. /// A function that returns the disposables to be created when the view model is activated. Cannot be null. - public static void WhenActivated(this IActivatableViewModel item, Func> block) // TODO: Create Test + public static void + WhenActivated(this IActivatableViewModel item, Func> block) { ArgumentExceptionHelper.ThrowIfNull(item); @@ -65,7 +72,7 @@ public static void WhenActivated(this IActivatableViewModel item, Action { - var ret = new List(); + List ret = []; block(ret.Add); return ret; }); @@ -81,13 +88,14 @@ public static void WhenActivated(this IActivatableViewModel item, ActionThe view model that supports activation and deactivation. Cannot be null. /// An action that receives a to which disposables can be added for automatic /// cleanup upon deactivation. Cannot be null. - public static void WhenActivated(this IActivatableViewModel item, Action block) // TODO: Create Test + public static void + WhenActivated(this IActivatableViewModel item, Action block) { ArgumentExceptionHelper.ThrowIfNull(item); item.Activator.AddActivationBlock(() => { - var d = new CompositeDisposable(); + CompositeDisposable d = []; block(d); return [d]; }); @@ -101,7 +109,8 @@ public static void WhenActivated(this IActivatableViewModel item, Action /// An that deactivates the view and disposes the registered disposables when disposed. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static IDisposable WhenActivated(this IActivatableView item, Func> block) // TODO: Create Test + public static IDisposable + WhenActivated(this IActivatableView item, Func> block) { ArgumentExceptionHelper.ThrowIfNull(item); @@ -125,26 +134,31 @@ public static IDisposable WhenActivated(this IActivatableView item, Func /// Thrown if is null or if activation cannot be determined for the specified type. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static IDisposable WhenActivated(this IActivatableView item, Func> block, IViewFor? view) // TODO: Create Test + public static IDisposable WhenActivated( + this IActivatableView item, + Func> block, + IViewFor? view) { ArgumentExceptionHelper.ThrowIfNull(item); var activationFetcher = _activationFetcherCache.Get(item.GetType()); if (activationFetcher is null) { + // In design mode there is no activation fetcher; drop the cache and no-op rather than throwing (see #4358). if (item.GetIsDesignMode()) { _activationFetcherCache.InvalidateAll(); - return Disposable.Empty; + return EmptyDisposable.Instance; } - const string msg = "Don't know how to detect when {0} is activated/deactivated, you may need to implement IActivationForViewFetcher"; - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, msg, item.GetType().FullName)); + const string Msg = + "Don't know how to detect when {0} is activated/deactivated, you may need to implement IActivationForViewFetcher"; + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, Msg, item.GetType().FullName)); } var activationEvents = activationFetcher.GetActivationForView(item); - var vmDisposable = Disposable.Empty; + IDisposable vmDisposable = EmptyDisposable.Instance; if ((view ?? item) is IViewFor v) { vmDisposable = HandleViewModelActivation(v, activationEvents); @@ -183,41 +197,53 @@ public static IDisposable WhenActivated(this IActivatableView item, ActionThe view instance associated with the activation. Cannot be null. /// An that deactivates the view and disposes all registered resources when disposed. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static IDisposable WhenActivated(this IActivatableView item, Action> block, IViewFor view) => // TODO: Create Test + public static IDisposable WhenActivated( + this IActivatableView item, + Action> block, + IViewFor view) => item.WhenActivated( - () => - { - var ret = new List(); - block(ret.Add); - return ret; - }, - view); + () => + { + List ret = []; + block(ret.Add); + return ret; + }, + view); /// /// Activates the specified view and executes the provided block when the view is activated, managing disposables /// for the activation lifecycle. /// - /// Use this method to manage subscriptions and other disposables that should be tied to the - /// activation and deactivation of the view. All disposables added to the provided - /// will be disposed when the returned is disposed, typically when the view is - /// deactivated. - /// The view that implements to be activated. - /// An action that receives a to which activation-related disposables should be - /// added. This block is executed when the view is activated. - /// An optional instance representing the view context. If not specified, the method uses the - /// as the view. - /// An that deactivates the view and disposes of all disposables added to the when disposed. + /// The view that implements IActivatableView to be activated. + /// An action that receives a CompositeDisposable to which activation-related disposables should be added. + /// An IDisposable that deactivates the view and disposes of all registered disposables when disposed. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static IDisposable WhenActivated(this IActivatableView item, Action block, IViewFor? view = null) => - item.WhenActivated( - () => - { - var d = new CompositeDisposable(); - block(d); - return [d]; - }, - view); + public static IDisposable WhenActivated( + this IActivatableView item, + Action block) => + item.WhenActivated(block, null); + + /// + /// Activates the specified view and executes the provided block when the view is activated, managing disposables + /// for the activation lifecycle. + /// + /// The view that implements IActivatableView to be activated. + /// An action that receives a CompositeDisposable to which activation-related disposables should be added. + /// An optional IViewFor instance representing the view context. If null, the item itself is used as the view. + /// An IDisposable that deactivates the view and disposes of all registered disposables when disposed. + [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + public static IDisposable WhenActivated( + this IActivatableView item, + Action block, + IViewFor? view) => + item.WhenActivated( + () => + { + CompositeDisposable d = []; + block(d); + return [d]; + }, + view); /// /// Gets a value indicating whether the view is currently being loaded by a designer surface. @@ -255,22 +281,24 @@ public static bool GetIsDesignMode(this IActivatableView item) /// activation and to indicate deactivation. /// A that manages the subscription to the activation observable and the /// disposables created by the block. Disposing this object cleans up all associated resources. - private static CompositeDisposable HandleViewActivation(Func> block, IObservable activation) + private static CompositeDisposable HandleViewActivation( + Func> block, + IObservable activation) { - var viewDisposable = new SerialDisposable(); + SwapDisposable viewDisposable = new(); - return new CompositeDisposable( - activation.Subscribe(activated => - { - // NB: We need to make sure to respect ordering so that the clean up - // happens before we invoke block again - viewDisposable.Disposable = Disposable.Empty; - if (activated) - { - viewDisposable.Disposable = new CompositeDisposable(block()); - } - }), - viewDisposable); + return new( + activation.Subscribe(new DelegateObserver(activated => + { + viewDisposable.Disposable = EmptyDisposable.Instance; + if (!activated) + { + return; + } + + viewDisposable.Disposable = new CompositeDisposable(block()); + })), + viewDisposable); } /// @@ -289,34 +317,33 @@ private static CompositeDisposable HandleViewActivation(Func activation) { - var vmDisposable = new SerialDisposable(); - var viewVmDisposable = new SerialDisposable(); + SwapDisposable vmDisposable = new(); + SwapDisposable viewVmDisposable = new(); + + return new( + activation.Subscribe(new DelegateObserver(activated => + { + if (activated) + { + viewVmDisposable.Disposable = view.WhenAnyValue(nameof(view.ViewModel)) + .Subscribe(new DelegateObserver(value => + { + vmDisposable.Disposable = EmptyDisposable.Instance; + if (value is not IActivatableViewModel activatable) + { + return; + } - return new CompositeDisposable( - activation.Subscribe(activated => - { - if (activated) - { - viewVmDisposable.Disposable = view.WhenAnyValue(nameof(view.ViewModel)) - .Select(x => x as IActivatableViewModel) - .Subscribe(x => - { - // NB: We need to make sure to respect ordering so that the clean up - // happens before we activate again - vmDisposable.Disposable = Disposable.Empty; - if (x is not null) - { - vmDisposable.Disposable = x.Activator.Activate(); - } - }); - } - else - { - viewVmDisposable.Disposable = Disposable.Empty; - vmDisposable.Disposable = Disposable.Empty; - } - }), - vmDisposable, - viewVmDisposable); + vmDisposable.Disposable = activatable.Activator.Activate(); + })); + } + else + { + viewVmDisposable.Disposable = EmptyDisposable.Instance; + vmDisposable.Disposable = EmptyDisposable.Instance; + } + })), + vmDisposable, + viewVmDisposable); } } diff --git a/src/ReactiveUI/Activation/ViewModelActivator.cs b/src/ReactiveUI/Activation/ViewModelActivator.cs index 39927552aa..dce7349c67 100644 --- a/src/ReactiveUI/Activation/ViewModelActivator.cs +++ b/src/ReactiveUI/Activation/ViewModelActivator.cs @@ -1,8 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; +using System.Reactive.Disposables; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -34,10 +39,19 @@ namespace ReactiveUI; /// public sealed class ViewModelActivator : IDisposable { + /// List of registered activation blocks run when the view model is activated. private readonly List>> _blocks; - private readonly Subject _activated; - private readonly Subject _deactivated; - private IDisposable _activationHandle = Disposable.Empty; + + /// Subject that emits each time the view model is activated. + private readonly BroadcastSubject _activated; + + /// Subject that emits each time the view model is deactivated. + private readonly BroadcastSubject _deactivated; + + /// Composite disposable that is replaced on each activation cycle. + private IDisposable _activationHandle = EmptyDisposable.Instance; + + /// Reference count tracking how many times Activate has been called without a matching Deactivate. private int _refCount; /// @@ -72,14 +86,20 @@ public IDisposable Activate() { if (Interlocked.Increment(ref _refCount) == 1) { - var disposable = new CompositeDisposable(_blocks.SelectMany(x => x())); + CompositeDisposable disposable = [.. _blocks.SelectMany(x => x())]; Interlocked.Exchange(ref _activationHandle, disposable).Dispose(); _activated.OnNext(Unit.Default); } - return Disposable.Create(() => Deactivate()); + return new ActionDisposable(Deactivate); } + /// + /// This method is called by the framework when the corresponding View + /// is deactivated. Respects the activation reference count. + /// + public void Deactivate() => Deactivate(false); + /// /// This method is called by the framework when the corresponding View /// is deactivated. @@ -88,17 +108,16 @@ public IDisposable Activate() /// Force the VM to be deactivated, even /// if more than one person called Activate. /// - public void Deactivate(bool ignoreRefCount = false) + public void Deactivate(bool ignoreRefCount) { if (ignoreRefCount) { Interlocked.Exchange(ref _refCount, 0); - Interlocked.Exchange(ref _activationHandle, Disposable.Empty).Dispose(); + Interlocked.Exchange(ref _activationHandle, EmptyDisposable.Instance).Dispose(); _deactivated.OnNext(Unit.Default); return; } - // Guard against going negative — only decrement if current value is > 0 int current; int next; do @@ -113,11 +132,13 @@ public void Deactivate(bool ignoreRefCount = false) } while (Interlocked.CompareExchange(ref _refCount, next, current) != current); - if (next == 0) + if (next != 0) { - Interlocked.Exchange(ref _activationHandle, Disposable.Empty).Dispose(); - _deactivated.OnNext(Unit.Default); + return; } + + Interlocked.Exchange(ref _activationHandle, EmptyDisposable.Instance).Dispose(); + _deactivated.OnNext(Unit.Default); } /// diff --git a/src/ReactiveUI/Bindings/BindingAffinity.cs b/src/ReactiveUI/Bindings/BindingAffinity.cs new file mode 100644 index 0000000000..c5289299a9 --- /dev/null +++ b/src/ReactiveUI/Bindings/BindingAffinity.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI; + +/// +/// Common affinity scores shared by binding type converters, command binders, property +/// observation factories and view activation fetchers. A higher value indicates a stronger +/// match; zero means the candidate does not apply. +/// +internal static class BindingAffinity +{ + /// The affinity returned by the built-in value and string type converters. + public const int DefaultInternalTypeConverter = 2; + + /// The affinity for binding to a type's conventional default event. + public const int DefaultEvent = 3; + + /// The affinity for an explicit or interface-based match, such as INotifyPropertyChanged or a named event. + public const int Explicit = 5; + + /// The affinity for a strong, exact-type match, such as IReactiveObject or ICanActivate. + public const int ExactType = 10; +} diff --git a/src/ReactiveUI/Bindings/BindingDirection.cs b/src/ReactiveUI/Bindings/BindingDirection.cs index c77246c63c..07e6867b93 100644 --- a/src/ReactiveUI/Bindings/BindingDirection.cs +++ b/src/ReactiveUI/Bindings/BindingDirection.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,5 +17,5 @@ public enum BindingDirection TwoWay, /// The binding is updated asynchronously one way from the ViewModel. - AsyncOneWay, + AsyncOneWay } diff --git a/src/ReactiveUI/Bindings/BindingTypeConverter.cs b/src/ReactiveUI/Bindings/BindingTypeConverter.cs index dcf6eda4cc..8a6fa1e92b 100644 --- a/src/ReactiveUI/Bindings/BindingTypeConverter.cs +++ b/src/ReactiveUI/Bindings/BindingTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -47,13 +47,11 @@ public abstract class BindingTypeConverter : IBindingTypeConverter - public abstract bool TryConvert(TFrom? from, object? conversionHint, [MaybeNullWhen(true)] out TTo? result); + public abstract bool TryConvert(TFrom? from, object? conversionHint, out TTo? result); /// public bool TryConvertTyped(object? from, object? conversionHint, out object? result) { - // Allow null inputs for converters whose source type can represent null, and - // permit null outputs when the target type is nullable/reference. TTo? typedResult; if (from is null) { diff --git a/src/ReactiveUI/Bindings/BindingTypeConverterDispatch.cs b/src/ReactiveUI/Bindings/BindingTypeConverterDispatch.cs index 3ea1b49252..0b894dd474 100644 --- a/src/ReactiveUI/Bindings/BindingTypeConverterDispatch.cs +++ b/src/ReactiveUI/Bindings/BindingTypeConverterDispatch.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -55,8 +55,6 @@ internal static bool TryConvert( var runtimeType = from.GetType(); var converterFromType = converter.FromType; - // Exact pair match keeps dispatch predictable and avoids assignability ambiguity, - // but allow nullable converters to accept boxed T values. if (converterFromType != runtimeType && Nullable.GetUnderlyingType(converterFromType) != runtimeType) { @@ -83,9 +81,11 @@ internal static bool TryConvert( /// true if the conversion was successful and the result is non-null; otherwise, false. internal static bool TryConvertFallback( IBindingFallbackConverter converter, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type fromType, object from, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type toType, object? conversionHint, out object? result) { @@ -93,21 +93,19 @@ internal static bool TryConvertFallback( ArgumentExceptionHelper.ThrowIfNull(from); ArgumentExceptionHelper.ThrowIfNull(toType); - // Delegate to fallback converter (from is guaranteed non-null) if (!converter.TryConvert(fromType, from, toType, conversionHint, out result)) { result = null; return false; } - // Fallback converters must still guarantee a non-null result on success. - if (result is null) + if (result is not null) { - result = null; - return false; + return true; } - return true; + result = null; + return false; } /// @@ -128,40 +126,42 @@ internal static bool TryConvertFallback( /// true if the conversion was successful and result contains the converted value; otherwise, false. internal static bool TryConvertAny( object? converter, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type fromType, object? from, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type toType, object? conversionHint, out object? result) { ArgumentExceptionHelper.ThrowIfNull(toType); - if (converter is null) - { - result = null; - return false; - } - - // Dispatch to typed converter - if (converter is IBindingTypeConverter typedConverter) + switch (converter) { - return TryConvert(typedConverter, from, toType, conversionHint, out result); + case null: + { + result = null; + return false; + } + + case IBindingTypeConverter typedConverter: + return TryConvert(typedConverter, from, toType, conversionHint, out result); + case IBindingFallbackConverter fallbackConverter: + { + if (from is null) + { + result = null; + return false; + } + + return TryConvertFallback(fallbackConverter, fromType, from, toType, conversionHint, out result); + } + + default: + { + result = null; + return false; + } } - - // Dispatch to fallback converter (requires non-null input) - if (converter is IBindingFallbackConverter fallbackConverter) - { - if (from is null) - { - result = null; - return false; - } - - return TryConvertFallback(fallbackConverter, fromType, from, toType, conversionHint, out result); - } - - // Unknown converter type - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Command/CommandBinder.cs b/src/ReactiveUI/Bindings/Command/CommandBinder.cs index 0c5a8cefa8..a2e9e3a068 100644 --- a/src/ReactiveUI/Bindings/Command/CommandBinder.cs +++ b/src/ReactiveUI/Bindings/Command/CommandBinder.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; using System.Windows.Input; namespace ReactiveUI; @@ -19,6 +20,9 @@ namespace ReactiveUI; /// scenarios. public static class CommandBinder { + /// + /// The command binder implementation resolved from the service locator or the default implementation. + /// private static readonly ICommandBinderImplementation _binderImplementation; /// @@ -28,47 +32,81 @@ public static class CommandBinder /// static members of the CommandBinder class are accessed. It attempts to retrieve an ICommandBinderImplementation /// from the application's service locator; if none is available, a default implementation is used. static CommandBinder() => _binderImplementation = AppLocator.Current.GetService() ?? - new CommandBinderImplementation(); + new CommandBinderImplementation(); + + /// + /// Binds a command from the view model to a control on the view using the default event and an observable parameter. + /// + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property, which must implement ICommand. + /// The type of the control on the view to which the command will be bound. + /// The type of the parameter passed to the command when it is executed. + /// The view instance to which the command will be bound. + /// The view model instance containing the command property. + /// An expression identifying the command property on the view model to bind. + /// An expression identifying the control on the view to which the command will be bound. + /// An observable that provides the parameter to pass to the command when it is executed. + /// An IReactiveBinding instance representing the active binding between the command and the control. + [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] + public static IReactiveBinding BindCommand< + TView, + TViewModel, + TProp, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, + TParam>( + this TView view, + TViewModel? viewModel, + Expression> propertyName, + Expression> controlName, + IObservable withParameter) + where TView : class, IViewFor + where TViewModel : class + where TProp : ICommand + where TControl : class => + BindCommand(view, viewModel, propertyName, controlName, withParameter, null); /// /// Binds a command from the view model to a control on the view, enabling the control to execute the command with a - /// specified parameter when an event is raised. + /// parameter when triggered. /// - /// - /// The binding enables the control to execute the command when the specified event is raised, - /// and automatically manages the enabled state of the control based on the command's CanExecute state. - /// This method uses reflection to observe events and properties on the control, which may be - /// affected by trimming in some deployment scenarios. - /// - /// The type of the view that implements the IViewFor interface. - /// The type of the view model containing the command to bind. - /// The type of the command property on the view model. Must implement ICommand. + /// This method uses reflection to dynamically observe events and properties on the control, + /// which may be affected by trimming in some deployment scenarios. The binding remains active until the returned + /// IReactiveBinding is disposed. + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property, which must implement ICommand. /// The type of the control on the view to which the command will be bound. - /// The type of the parameter passed to the command when the event is raised. - /// The view instance containing the control to which the command will be bound. Cannot be null. - /// The view model instance containing the command to bind. Used for type inference. - /// Can be null if the binding should be established without an initial view model. + /// The type of the parameter passed to the command when it is executed. + /// The view instance to which the command will be bound. Cannot be null. + /// The view model instance containing the command property. May be null if the view is not currently bound to a + /// view model. /// An expression identifying the command property on the view model to bind. Cannot be null. /// An expression identifying the control on the view to which the command will be bound. Cannot be null. /// An observable that provides the parameter to pass to the command when it is executed. Cannot be null. - /// The name of the event on the control that triggers the command execution. If null, a default event is used based - /// on the control type. If the specified event does not exist on the control, an exception may be thrown at runtime. + /// The name of the event on the control that triggers the command. If null, a default event is used based on the + /// control type. /// NOTE: If this parameter is used inside WhenActivated, it's important to dispose the binding when the view is deactivated. - /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. - /// It will remain active until disposed. + /// An IReactiveBinding instance representing the active binding between the command and the control. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] public static IReactiveBinding BindCommand< TView, TViewModel, TProp, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, TParam>( this TView view, TViewModel? viewModel, Expression> propertyName, Expression> controlName, IObservable withParameter, - string? toEvent = null) + string? toEvent) where TView : class, IViewFor where TViewModel : class where TProp : ICommand @@ -83,40 +121,70 @@ public static IReactiveBinding BindCommand< } /// - /// Binds a command from the view model to a control on the view, enabling the control to execute the command with a - /// specified parameter when an event is raised. + /// Binds a command from the view model to a control on the view using the default event. /// - /// - /// The binding enables the control to execute the command when the specified event is raised, - /// and automatically manages the enabled state of the control based on the command's CanExecute state. - /// This method uses reflection to observe events and properties on the control, which may be - /// affected by trimming in some deployment scenarios. - /// - /// The type of the view that implements the IViewFor interface. - /// The type of the view model containing the command to bind. - /// The type of the command property on the view model. Must implement ICommand. + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property to bind, implementing ICommand. /// The type of the control on the view to which the command will be bound. - /// The view instance containing the control to which the command will be bound. Cannot be null. - /// The view model instance containing the command to bind. Used for type inference. - /// Can be null if the binding should be established without an initial view model. + /// The view instance to which the control belongs. + /// The view model instance containing the command property. + /// An expression identifying the command property on the view model to bind. + /// An expression identifying the control on the view to bind the command to. + /// An object representing the binding between the command and the control, which can be disposed to unbind. + [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] + public static IReactiveBinding BindCommand< + TView, + TViewModel, + TProp, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl>( + this TView view, + TViewModel? viewModel, + Expression> propertyName, + Expression> controlName) + where TView : class, IViewFor + where TViewModel : class + where TProp : ICommand + where TControl : class => + BindCommand(view, viewModel, propertyName, controlName, (string?)null); + + /// + /// Binds a command from the view model to a control on the view, enabling the control to execute the specified + /// command when triggered. + /// + /// This method uses reflection to observe events and properties on the control and may be + /// affected by trimming in environments that remove unused members. The binding enables the control to execute the + /// command when the specified event is raised, and automatically manages the enabled state of the control based on + /// the command's CanExecute state. + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property to bind, implementing ICommand. + /// The type of the control on the view to which the command will be bound. + /// The view instance to which the control belongs. Cannot be null. + /// The view model instance containing the command property. Can be null if the view's ViewModel property is used. /// An expression identifying the command property on the view model to bind. Cannot be null. - /// An expression identifying the control on the view to which the command will be bound. Cannot be null. - /// The name of the event on the control that triggers the command execution. If null, a default event is used based - /// on the control type. If the specified event does not exist on the control, an exception may be thrown at runtime. + /// An expression identifying the control on the view to bind the command to. Cannot be null. + /// The name of the event on the control that triggers the command. If null, a default event is used based on the + /// control type. /// NOTE: If this parameter is used inside WhenActivated, it's important to dispose the binding when the view is deactivated. - /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. - /// It will remain active until disposed. + /// An object representing the binding between the command and the control, which can be disposed to unbind. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] public static IReactiveBinding BindCommand< TView, TViewModel, TProp, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl>( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl>( this TView view, TViewModel? viewModel, Expression> propertyName, Expression> controlName, - string? toEvent = null) + string? toEvent) where TView : class, IViewFor where TViewModel : class where TProp : ICommand @@ -129,45 +197,79 @@ public static IReactiveBinding BindCommand< return _binderImplementation.BindCommand(viewModel, view, propertyName, controlName, toEvent); } + /// + /// Binds a command from the view model to a control using the default event and a view model expression parameter. + /// + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property, typically implementing ICommand. + /// The type of the control on the view to which the command will be bound. + /// The type of the parameter passed to the command when it is executed. + /// The view instance containing the control to bind the command to. + /// The view model instance containing the command property. + /// An expression identifying the command property on the view model to bind. + /// An expression identifying the control on the view to which the command will be bound. + /// An expression specifying the parameter to pass to the command when it is executed. + /// An IReactiveBinding representing the established binding between the command and the control. + [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] + public static IReactiveBinding BindCommand< + TView, + TViewModel, + TProp, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, + TParam>( + this TView view, + TViewModel? viewModel, + Expression> propertyName, + Expression> controlName, + Expression> withParameter) + where TView : class, IViewFor + where TViewModel : class + where TProp : ICommand + where TControl : class => + BindCommand(view, viewModel, propertyName, controlName, withParameter, null); + /// /// Binds a command from the view model to a control on the view, enabling the control to execute the command with a - /// specified parameter when an event is raised. + /// specified parameter when triggered. /// - /// - /// The binding enables the control to execute the command when the specified event is raised, - /// and automatically manages the enabled state of the control based on the command's CanExecute state. - /// This method uses reflection to observe events and properties on the control, which may be - /// affected by trimming in some deployment scenarios. - /// - /// The type of the view that implements the IViewFor interface. - /// The type of the view model containing the command to bind. - /// The type of the command property on the view model. Must implement ICommand. + /// This method uses reflection to observe events and properties on the control and view model, + /// which may be affected by trimming in some deployment scenarios. The binding remains active until the returned + /// IReactiveBinding is disposed. + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property, typically implementing ICommand. /// The type of the control on the view to which the command will be bound. - /// The type of the parameter passed to the command when the event is raised. - /// The view instance containing the control to which the command will be bound. Cannot be null. - /// The view model instance containing the command to bind. Used for type inference. - /// Can be null if the binding should be established without an initial view model. + /// The type of the parameter passed to the command when it is executed. + /// The view instance containing the control to bind the command to. Cannot be null. + /// The view model instance containing the command property. May be null if the view is not currently bound to a + /// view model. /// An expression identifying the command property on the view model to bind. Cannot be null. /// An expression identifying the control on the view to which the command will be bound. Cannot be null. /// An expression specifying the parameter to pass to the command when it is executed. Cannot be null. /// The name of the event on the control that triggers the command execution. If null, a default event is used based - /// on the control type. If the specified event does not exist on the control, an exception may be thrown at runtime. + /// on the control type. /// NOTE: If this parameter is used inside WhenActivated, it's important to dispose the binding when the view is deactivated. - /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. - /// It will remain active until disposed. + /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] public static IReactiveBinding BindCommand< TView, TViewModel, TProp, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, TParam>( - this TView view, - TViewModel? viewModel, - Expression> propertyName, - Expression> controlName, - Expression> withParameter, - string? toEvent = null) + this TView view, + TViewModel? viewModel, + Expression> propertyName, + Expression> controlName, + Expression> withParameter, + string? toEvent) where TView : class, IViewFor where TViewModel : class where TProp : ICommand diff --git a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs index 9b19cb5195..a23efc90c5 100644 --- a/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Command/CommandBinderImplementation.cs @@ -1,10 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reactive.Disposables; using System.Windows.Input; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -25,20 +29,45 @@ namespace ReactiveUI; /// public class CommandBinderImplementation : ICommandBinderImplementation { - /// + /// + /// Binds a command from the view model to a control on the view, enabling the control to execute the command with + /// an optional parameter when triggered by a specified event. + /// + /// This method uses reflection to observe properties and events, which may be affected by + /// trimming in some deployment scenarios. The binding is one-way, from the view model command to the view control. + /// If the specified event is not found on the control, an exception may be thrown at runtime. + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property to bind, implementing ICommand. + /// The type of the control on the view to which the command will be bound. + /// The type of the parameter passed to the command when it is executed. + /// The view model instance containing the command to bind. Can be null if the binding should be established without + /// an initial view model. + /// The view instance containing the control to which the command will be bound. Cannot be null. + /// An expression specifying the command property on the view model to bind. Cannot be null. + /// An expression specifying the control on the view to which the command will be bound. Cannot be null. + /// An expression specifying the parameter to pass to the command when it is executed. Can be null if the command + /// does not require a parameter. + /// The name of the event on the control that triggers the command execution. If null, a default event is used based + /// on the control type. + /// An IReactiveBinding{TView, TProp} representing the established command binding. Disposing the returned object + /// will remove the binding. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] public IReactiveBinding BindCommand< TView, TViewModel, TProp, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, TParam>( - TViewModel? viewModel, - TView view, - Expression> vmProperty, - Expression> controlProperty, - Expression> withParameter, - string? toEvent = null) + TViewModel? viewModel, + TView view, + Expression> vmProperty, + Expression> controlProperty, + Expression> withParameter, + string? toEvent = null) where TView : class, IViewFor where TViewModel : class where TProp : ICommand @@ -46,20 +75,17 @@ public IReactiveBinding BindCommand< { ArgumentExceptionHelper.ThrowIfNull(vmProperty); ArgumentExceptionHelper.ThrowIfNull(controlProperty); - ArgumentExceptionHelper.ThrowIfNull(withParameter); var vmExpression = Reflection.Rewrite(vmProperty.Body); var controlExpression = Reflection.Rewrite(controlProperty.Body); - var parameterExpression = Reflection.Rewrite(withParameter.Body); - var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast(); - var parameterObservable = Reflection.ViewModelWhenAnyValue(viewModel, view, parameterExpression).Cast(); + var source = new SelectObservable(Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression), static x => (TProp)x!); var bindingDisposable = BindCommandInternal( source, view, controlExpression, - parameterObservable, + withParameter.ToObservable(viewModel), toEvent); return new ReactiveBinding( @@ -71,20 +97,47 @@ public IReactiveBinding BindCommand< bindingDisposable); } - /// + /// + /// Binds a command from the view model to a control on the view, enabling the control to execute the command with + /// an optional parameter stream and event trigger. + /// + /// This method uses reflection to observe and bind to members, which may be affected by trimming + /// in some environments. The binding is one-way, from the view model command to the view control. If the control or + /// command property is not found, the binding will not be established. The method is suitable for scenarios where + /// commands need to be dynamically bound to controls with support for parameter streams and custom event + /// triggers. + /// The type of the view implementing the IViewFor interface. + /// The type of the view model containing the command property. + /// The type of the command property, which must implement ICommand. + /// The type of the control on the view to which the command will be bound. + /// The type of the parameter passed to the command when it is executed. + /// The view model instance containing the command to bind. Can be null if the view model is not available at + /// binding time. + /// The view instance containing the control to which the command will be bound. + /// An expression specifying the command property on the view model to bind. + /// An expression specifying the control on the view that will trigger the command. + /// An observable sequence providing the parameter to pass to the command when it is executed. The latest value is + /// used for each command invocation. + /// The name of the event on the control that triggers the command. If null, a default event is used based on the + /// control type. + /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. + /// Disposing the binding will remove the command association. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] public IReactiveBinding BindCommand< TView, TViewModel, TProp, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, TParam>( - TViewModel? viewModel, - TView view, - Expression> vmProperty, - Expression> controlProperty, - IObservable withParameter, - string? toEvent = null) + TViewModel? viewModel, + TView view, + Expression> vmProperty, + Expression> controlProperty, + IObservable withParameter, + string? toEvent = null) where TView : class, IViewFor where TViewModel : class where TProp : ICommand @@ -96,7 +149,7 @@ public IReactiveBinding BindCommand< var vmExpression = Reflection.Rewrite(vmProperty.Body); var controlExpression = Reflection.Rewrite(controlProperty.Body); - var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast(); + var source = new SelectObservable(Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression), static x => (TProp)x!); var bindingDisposable = BindCommandInternal( source, @@ -134,77 +187,58 @@ public IReactiveBinding BindCommand< /// The name of the event on the control to bind the command to. If null or empty, the default event is used. /// An IDisposable that can be used to unbind the command and release associated resources. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - private static IDisposable BindCommandInternal< + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + private static CompositeDisposable BindCommandInternal< TView, TProp, TParam, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl>( - IObservable source, - TView view, - Expression controlExpression, - IObservable withParameter, - string? toEvent) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl>( + IObservable source, + TView view, + Expression controlExpression, + IObservable withParameter, + string? toEvent) where TView : class, IViewFor where TProp : ICommand where TControl : class { - // SerialDisposable safely replaces and disposes the previous binding when a new one is assigned. - var currentBinding = new SerialDisposable(); + SwapDisposable currentBinding = new(); var currentControl = default(TControl); var isInitialBind = true; - - // Check for optional platform-specific command rebinding customization - var rebindingCustomizer = string.IsNullOrEmpty(toEvent) - ? AppLocator.Current.GetService() - : null; - - // Cache boxing of parameter values once to avoid rebuilding the Select pipeline on every rebind. - var boxedParameter = withParameter.Select(static p => (object?)p); - - // Observe the control expression chain and extract the current control instance. - var controlValues = + var rebindingCustomizer = AppLocator.Current.GetService(); + var boxedParameter = new SelectObservable(withParameter, static p => (object?)p); + var controlValues = new SelectObservable, object?>( view.SubscribeToExpressionChain( - controlExpression, - beforeChange: false, - skipInitial: false, - suppressWarnings: false) - .Select(static x => x.GetValue()); - - // CombineLatest ensures rebinding occurs when either the command or control changes. - // ValueTuple avoids per-notification heap allocations. - var bindInfo = source.CombineLatest(controlValues, static (command, host) => (command, host)); + controlExpression, + false, + false, + false), + static x => x.GetValue()); + var bindInfo = new CombineLatest2Observable(source, controlValues, static (command, host) => (command, host)); - var subscription = bindInfo.Subscribe(tuple => + var subscription = bindInfo.Subscribe(new DelegateObserver<(TProp command, object? host)>(tuple => { var (command, host) = tuple; - - // Preserve existing behavior: if the control is currently null, - // do not tear down or recreate the existing binding. if (host is null) { return; } - // Match original semantics: allow null if the cast fails. var control = host as TControl; - - // Try platform-specific optimization: if only the command changed (not the control), - // attempt to update the command directly without full rebind var isSameControl = !isInitialBind && ReferenceEquals(control, currentControl); - if (isSameControl && control is not null && rebindingCustomizer is not null) + if (isSameControl && control is not null && rebindingCustomizer?.TryUpdateCommand(control, command) is true) { - if (rebindingCustomizer.TryUpdateCommand(control, command)) - { - // Successfully updated command without rebinding - return; - } + return; } - // Full rebind (first bind, control changed, or customizer not available/failed) isInitialBind = false; currentControl = control; - - // Assigning to SerialDisposable disposes the previous binding deterministically. currentBinding.Disposable = !string.IsNullOrEmpty(toEvent) ? CreatesCommandBinding.BindCommandToObject( @@ -216,9 +250,250 @@ private static IDisposable BindCommandInternal< command, control, boxedParameter); - }); + })); + + return new(subscription, currentBinding); + } + + /// Projects each value of a source through a selector. Specialised command-binding projection. + /// The source element type. + /// The projected element type. + /// The source observable. + /// Projects a source value into a result. + private sealed class SelectObservable(IObservable source, Func selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } + + /// Applies the selector to each value and forwards the result. + /// The observer receiving projected values. + /// Projects a source value into a result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver + { + /// + public void OnNext(TIn value) + { + TOut result; + try + { + result = selector(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// + /// Combines the latest value of two sources through a selector, emitting once both have produced a value. + /// Specialised command-binding combine-latest. + /// + /// The first source element type. + /// The second source element type. + /// The combined result type. + /// The first source. + /// The second source. + /// Combines the latest value of each source. + private sealed class CombineLatest2Observable( + IObservable first, + IObservable second, + Func selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, selector, first, second); + } + + /// Tracks the latest value of each source and emits the combination once both have reported. + private sealed class Sink : IDisposable + { + /// The number of combined sources. + private const int SourceCount = 2; + + /// Guards the latest values and the completion counter. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving the combined values. + private readonly IObserver _downstream; + + /// Combines the latest value of each source. + private readonly Func _selector; + + /// The subscription to the first source. + private readonly IDisposable _firstSubscription; + + /// The subscription to the second source. + private readonly IDisposable _secondSubscription; + + /// The latest value of the first source. + private T1 _latest1 = default!; + + /// The latest value of the second source. + private T2 _latest2 = default!; - // Dispose ordering: stop producing new bindings first, then dispose the active binding. - return new CompositeDisposable(subscription, currentBinding); + /// Whether the first source has reported a value. + private bool _has1; + + /// Whether the second source has reported a value. + private bool _has2; + + /// The number of sources that have completed. + private int _doneCount; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Initializes a new instance of the class and subscribes to both sources. + /// The observer receiving the combined values. + /// Combines the latest value of each source. + /// The first source. + /// The second source. + public Sink(IObserver downstream, Func selector, IObservable first, IObservable second) + { + _downstream = downstream; + _selector = selector; + _firstSubscription = first.Subscribe(new FirstObserver(this)); + _secondSubscription = second.Subscribe(new SecondObserver(this)); + } + + /// + public void Dispose() + { + _firstSubscription.Dispose(); + _secondSubscription.Dispose(); + } + + /// Records the first source's latest value and emits if both are present. + /// The reported value. + private void OnNext1(T1 value) + { + TResult result; + lock (_gate) + { + if (_stopped) + { + return; + } + + _latest1 = value; + _has1 = true; + if (!_has2) + { + return; + } + + result = _selector(_latest1, _latest2); + } + + _downstream.OnNext(result); + } + + /// Records the second source's latest value and emits if both are present. + /// The reported value. + private void OnNext2(T2 value) + { + TResult result; + lock (_gate) + { + if (_stopped) + { + return; + } + + _latest2 = value; + _has2 = true; + if (!_has1) + { + return; + } + + result = _selector(_latest1, _latest2); + } + + _downstream.OnNext(result); + } + + /// Forwards an error from either source. + /// The error to forward. + private void OnErrorAny(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _downstream.OnError(error); + } + + /// Completes the downstream once both sources have completed. + private void OnCompletedAny() + { + lock (_gate) + { + if (_stopped || ++_doneCount < SourceCount) + { + return; + } + + _stopped = true; + } + + _downstream.OnCompleted(); + } + + /// Routes the first source's notifications to the parent sink. + /// The owning sink. + private sealed class FirstObserver(Sink parent) : IObserver + { + /// + public void OnNext(T1 value) => parent.OnNext1(value); + + /// + public void OnError(Exception error) => parent.OnErrorAny(error); + + /// + public void OnCompleted() => parent.OnCompletedAny(); + } + + /// Routes the second source's notifications to the parent sink. + /// The owning sink. + private sealed class SecondObserver(Sink parent) : IObserver + { + /// + public void OnNext(T2 value) => parent.OnNext2(value); + + /// + public void OnError(Exception error) => parent.OnErrorAny(error); + + /// + public void OnCompleted() => parent.OnCompletedAny(); + } + } } } diff --git a/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs b/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs index 23ece67f1c..2f9a50c0d0 100644 --- a/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs +++ b/src/ReactiveUI/Bindings/Command/CommandBinderImplementationMixins.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reactive.Linq; using System.Windows.Input; namespace ReactiveUI; @@ -38,16 +40,23 @@ internal static class CommandBinderImplementationMixins /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. /// It will remain active until disposed. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] - public static IReactiveBinding BindCommand( + public static IReactiveBinding BindCommand< + TView, + TViewModel, + TProp, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl>( this ICommandBinderImplementation @this, TViewModel? viewModel, TView view, Expression> propertyName, Expression> controlName, string? toEvent = null) - where TView : class, IViewFor - where TViewModel : class - where TProp : ICommand - where TControl : class => + where TView : class, IViewFor + where TViewModel : class + where TProp : ICommand + where TControl : class => @this.BindCommand(viewModel, view, propertyName, controlName, Observable.Empty, toEvent); } diff --git a/src/ReactiveUI/Bindings/Command/CreatesCommandBinding.cs b/src/ReactiveUI/Bindings/Command/CreatesCommandBinding.cs index f5e69c5533..685b294201 100644 --- a/src/ReactiveUI/Bindings/Command/CreatesCommandBinding.cs +++ b/src/ReactiveUI/Bindings/Command/CreatesCommandBinding.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -26,13 +26,16 @@ internal static class CreatesCommandBinding /// An IDisposable that can be used to unbind the command from the target object. /// Thrown if a suitable command binder cannot be found for the specified target type. [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - public static IDisposable BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl>(ICommand? command, TControl? target, IObservable commandParameter) + public static IDisposable BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl>(ICommand? command, TControl? target, IObservable commandParameter) where TControl : class { - var binder = GetBinder(hasEventTarget: false); - var ret = binder.BindCommandToObject(command, target, commandParameter) - ?? throw new Exception($"Couldn't bind Command Binder for {typeof(TControl).FullName}"); - return ret; + var binder = GetBinder(false); + return binder.BindCommandToObject(command, target, commandParameter) + ?? throw new InvalidOperationException($"Couldn't bind Command Binder for {typeof(TControl).FullName}"); } /// @@ -53,17 +56,24 @@ internal static class CreatesCommandBinding /// An IDisposable that can be used to unbind the command from the event. /// Thrown if a suitable command binder cannot be found for the specified target type and event name. [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - public static IDisposable BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] TControl, TEventArgs>( + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IDisposable BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, TEventArgs>( ICommand? command, TControl? target, IObservable commandParameter, string eventName) where TControl : class { - var binder = GetBinder(hasEventTarget: true); - var ret = binder.BindCommandToObject(command, target, commandParameter, eventName) - ?? throw new Exception($"Couldn't bind Command Binder for {typeof(TControl).FullName} and event {eventName}"); - return ret; + var binder = GetBinder(true); + return binder.BindCommandToObject(command, target, commandParameter, eventName) + ?? throw new InvalidOperationException($"Couldn't bind Command Binder for {typeof(TControl).FullName} and event {eventName}"); } /// @@ -74,15 +84,23 @@ internal static class CreatesCommandBinding /// true if the target object exposes an event to bind to; otherwise, false. /// An instance of ICreatesCommandBinding that is best suited for the specified target type. /// Thrown if no suitable command binding provider can be found for the specified target type. - private static ICreatesCommandBinding GetBinder<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] T>(bool hasEventTarget) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + private static ICreatesCommandBinding GetBinder< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + T>(bool hasEventTarget) { var binder = AppLocator.Current.GetServices() .Aggregate((score: 0, binding: (ICreatesCommandBinding?)null), (acc, x) => { var score = x.GetAffinityForObject(hasEventTarget); - return (score > acc.score) ? (score, x) : acc; + return score > acc.score ? (score, x) : acc; }).binding; - return binder ?? throw new Exception($"Couldn't find a Command Binder for {typeof(T).FullName}"); + return binder ?? throw new InvalidOperationException($"Couldn't find a Command Binder for {typeof(T).FullName}"); } } diff --git a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs index 4182e470aa..9e451beb88 100644 --- a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs +++ b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaCommandParameter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -7,221 +7,232 @@ using System.Runtime.CompilerServices; using System.Windows.Input; -namespace ReactiveUI +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// +/// Creates command bindings for objects that expose Command and CommandParameter +/// as public instance properties. +/// +/// +/// +/// This binder targets command-source style controls (for example, WPF-style controls) where command execution +/// is driven by setting properties rather than subscribing to an event. +/// +/// +/// Trimming/AOT note: This type uses name-based reflection to locate public properties. Consumers running under +/// trimming must ensure the relevant public properties are preserved on the target control types. This +/// requirement is expressed via on the public generic entry points. +/// +/// +/// Performance note: This implementation uses a per-closed-generic static cache (“holder”) rather than a global MRU. +/// Steady-state access is lock-free and reduces lookup overhead to static field reads. +/// +/// +public sealed class CreatesCommandBindingViaCommandParameter : ICreatesCommandBinding { /// - /// Creates command bindings for objects that expose Command and CommandParameter - /// as public instance properties. + /// The expected name of the command property. /// + private const string CommandPropertyName = "Command"; + + /// + /// The expected name of the command parameter property. + /// + private const string CommandParameterPropertyName = "CommandParameter"; + + /// + /// + /// If an explicit event target exists, this binder is not applicable and returns 0. + /// Otherwise, it returns 5 if the target type exposes the required public instance properties; otherwise it returns 0. + /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public int GetAffinityForObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + T>(bool hasEventTarget) + { + if (hasEventTarget) + { + return 0; + } + + return Holder.HasRequiredProperties ? BindingAffinity.Explicit : 0; + } + + /// /// /// - /// This binder targets command-source style controls (for example, WPF-style controls) where command execution - /// is driven by setting properties rather than subscribing to an event. + /// This implementation is intentionally “best effort.” If required properties cannot be resolved for + /// , it returns to preserve legacy behavior where binder + /// selection is expected to be affinity-driven rather than exception-driven. /// /// - /// Trimming/AOT note: This type uses name-based reflection to locate public properties. Consumers running under - /// trimming must ensure the relevant public properties are preserved on the target control types. This - /// requirement is expressed via on the public generic entry points. + /// Disposal ordering minimizes observable races: the parameter subscription is disposed before restoring + /// the original parameter value. /// /// - /// Performance note: This implementation uses a per-closed-generic static cache (“holder”) rather than a global MRU. - /// Steady-state access is lock-free and reduces lookup overhead to static field reads. + /// The command property is set after establishing the parameter subscription, preserving historical ordering + /// semantics (“set Command last”). /// /// - public sealed class CreatesCommandBindingViaCommandParameter : ICreatesCommandBinding + [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] + public IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T>( + ICommand? command, + T? target, + IObservable commandParameter) + where T : class { - /// - /// The expected name of the command property. - /// - private const string CommandPropertyName = "Command"; - - /// - /// The expected name of the command parameter property. - /// - private const string CommandParameterPropertyName = "CommandParameter"; + ArgumentExceptionHelper.ThrowIfNull(target); + ArgumentExceptionHelper.ThrowIfNull(commandParameter); - /// - /// - /// If an explicit event target exists, this binder is not applicable and returns 0. - /// Otherwise, it returns 5 if the target type exposes the required public instance properties; otherwise it returns 0. - /// - public int GetAffinityForObject< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] T>(bool hasEventTarget) + var commandProperty = Holder.CommandProperty; + var commandParameterProperty = Holder.CommandParameterProperty; + if (commandProperty is null || commandParameterProperty is null) { - if (hasEventTarget) - { - return 0; - } - - return Holder.HasRequiredProperties ? 5 : 0; + return EmptyDisposable.Instance; } - /// - /// - /// - /// This implementation is intentionally “best effort.” If required properties cannot be resolved for - /// , it returns to preserve legacy behavior where binder - /// selection is expected to be affinity-driven rather than exception-driven. - /// - /// - /// Disposal ordering minimizes observable races: the parameter subscription is disposed before restoring - /// the original parameter value. - /// - /// - /// The command property is set after establishing the parameter subscription, preserving historical ordering - /// semantics (“set Command last”). - /// - /// - [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - public IDisposable? BindCommandToObject< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T>( - ICommand? command, - T? target, - IObservable commandParameter) - where T : class + var originalCommand = commandProperty.GetValue(target); + var originalParameter = commandParameterProperty.GetValue(target); + var subscription = commandParameter.Subscribe(new DelegateObserver(value => commandParameterProperty.SetValue(target, value))); + commandProperty.SetValue(target, command); + return new ActionDisposable(() => { - ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentExceptionHelper.ThrowIfNull(commandParameter); - - var commandProperty = Holder.CommandProperty; - var commandParameterProperty = Holder.CommandParameterProperty; - - // Preserve historical behavior: silently no-op if properties are missing. - if (commandProperty is null || commandParameterProperty is null) - { - return Disposable.Empty; - } + subscription.Dispose(); + commandParameterProperty.SetValue(target, originalParameter); + commandProperty.SetValue(target, originalCommand); + }); + } - // Snapshot original values once. Restoration occurs on disposal. - var originalCommand = commandProperty.GetValue(target); - var originalParameter = commandParameterProperty.GetValue(target); + /// + /// + /// This binder is for command-property based binding. If an event name is specified, event-based binders + /// should be used. This method therefore returns . + /// + [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IDisposable? BindCommandToObject( + ICommand? command, + T? target, + IObservable commandParameter, + string eventName) + where T : class => EmptyDisposable.Instance; + + /// + /// + /// This binder is for command-property based binding. If an event name is specified, event-based binders + /// should be used. This method therefore returns . + /// + public IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T, TEventArgs>( + ICommand? command, + T? target, + IObservable commandParameter, + Action> addHandler, + Action> removeHandler) + where T : class + where TEventArgs : EventArgs => EmptyDisposable.Instance; - // Subscribe first so dispose can stop updates before restoration. - // This delegate intentionally remains simple and allocation-minimal (no LINQ). - var subscription = commandParameter.Subscribe( - value => commandParameterProperty.SetValue(target, value)); + /// + /// Per-closed-generic cache of resolved command properties for a target type . + /// + /// + /// The target type. Public properties must be preserved in trimmed applications. + /// + /// + /// + /// This pattern avoids a global type-keyed cache that performs reflection in a cache factory delegate, + /// which is a frequent source of trimming warnings and hard-to-annotate member requirements. + /// + /// + /// Static initialization is thread-safe by the CLR. After initialization, access is lock-free. + /// + /// + private static class Holder< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] + T> + { + /// + /// Gets a value indicating whether the target type exposes both required properties. + /// + internal static readonly bool HasRequiredProperties; - // Set command last to preserve ordering semantics. - commandProperty.SetValue(target, command); + /// + /// Gets the resolved public instance property named Command, or if missing. + /// + internal static readonly PropertyInfo? CommandProperty; - // Use Rx disposable to unbind and restore original values. AnonymousDisposable is idempotent and thread-safe. - return Disposable.Create(() => - { - // Stop parameter updates first. - subscription.Dispose(); + /// + /// Gets the resolved public instance property named CommandParameter, or if missing. + /// + internal static readonly PropertyInfo? CommandParameterProperty; - // Restore original values in a predictable order. - commandParameterProperty.SetValue(target, originalParameter); - commandProperty.SetValue(target, originalCommand); - }); + /// + /// Initializes static members of the class. + /// + static Holder() + { + ResolveProperties(typeof(T), out CommandProperty, out CommandParameterProperty); + HasRequiredProperties = CommandProperty is not null && CommandParameterProperty is not null; } - /// - /// - /// This binder is for command-property based binding. If an event name is specified, event-based binders - /// should be used. This method therefore returns . - /// - [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - public IDisposable? BindCommandToObject( - ICommand? command, - T? target, - IObservable commandParameter, - string eventName) - where T : class => Disposable.Empty; - - /// - /// - /// This binder is for command-property based binding. If an event name is specified, event-based binders - /// should be used. This method therefore returns . - /// - public IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T, TEventArgs>(ICommand? command, T? target, IObservable commandParameter, Action> addHandler, Action> removeHandler) - where T : class - where TEventArgs : EventArgs => Disposable.Empty; - /// - /// Per-closed-generic cache of resolved command properties for a target type . + /// Resolves required properties via a single pass over public instance properties. /// - /// - /// The target type. Public properties must be preserved in trimmed applications. - /// + /// The target type to inspect. + /// Receives the resolved Command property, if present. + /// Receives the resolved CommandParameter property, if present. /// - /// - /// This pattern avoids a global type-keyed cache that performs reflection in a cache factory delegate, - /// which is a frequent source of trimming warnings and hard-to-annotate member requirements. - /// - /// - /// Static initialization is thread-safe by the CLR. After initialization, access is lock-free. - /// + /// The method avoids LINQ and repeated reflection calls to reduce overhead. /// - private static class Holder< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ResolveProperties( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] + Type type, + out PropertyInfo? command, + out PropertyInfo? commandParameter) { - /// - /// Gets a value indicating whether the target type exposes both required properties. - /// - internal static readonly bool HasRequiredProperties; - - /// - /// Gets the resolved public instance property named Command, or if missing. - /// - internal static readonly PropertyInfo? CommandProperty; - - /// - /// Gets the resolved public instance property named CommandParameter, or if missing. - /// - internal static readonly PropertyInfo? CommandParameterProperty; - - /// - /// Initializes static members of the class. - /// - static Holder() - { - ResolveProperties(typeof(T), out CommandProperty, out CommandParameterProperty); - HasRequiredProperties = CommandProperty is not null && CommandParameterProperty is not null; - } + command = null; + commandParameter = null; - /// - /// Resolves required properties via a single pass over public instance properties. - /// - /// The target type to inspect. - /// Receives the resolved Command property, if present. - /// Receives the resolved CommandParameter property, if present. - /// - /// The method avoids LINQ and repeated reflection calls to reduce overhead. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ResolveProperties( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, - out PropertyInfo? command, - out PropertyInfo? commandParameter) + var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); + + for (var i = 0; i < properties.Length; i++) { - command = null; - commandParameter = null; + var p = properties[i]; + var name = p.Name; + + if (command is null && + string.Equals(name, CommandPropertyName, StringComparison.Ordinal)) + { + command = p; + continue; + } - var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); + if (commandParameter is null && + string.Equals(name, CommandParameterPropertyName, StringComparison.Ordinal)) + { + commandParameter = p; + } - for (var i = 0; i < properties.Length; i++) + if (command is not null && commandParameter is not null) { - var p = properties[i]; - var name = p.Name; - - if (command is null && - string.Equals(name, CommandPropertyName, StringComparison.Ordinal)) - { - command = p; - continue; - } - - if (commandParameter is null && - string.Equals(name, CommandParameterPropertyName, StringComparison.Ordinal)) - { - commandParameter = p; - } - - if (command is not null && commandParameter is not null) - { - return; - } + return; } } } diff --git a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs index d587e45f58..2e6df9fb84 100644 --- a/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs +++ b/src/ReactiveUI/Bindings/Command/CreatesCommandBindingViaEvent.cs @@ -1,12 +1,15 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Disposables; using System.Reflection; using System.Runtime.CompilerServices; using System.Windows.Input; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -34,21 +37,26 @@ private static readonly (string Name, Type ArgsType)[] DefaultEventsToBind = [ ("Click", typeof(EventArgs)), ("TouchUpInside", typeof(EventArgs)), - ("MouseUp", typeof(EventArgs)), + ("MouseUp", typeof(EventArgs)) ]; /// [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public int GetAffinityForObject< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)]T>(bool hasEventTarget) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + T>(bool hasEventTarget) { if (hasEventTarget) { - return 5; + return BindingAffinity.Explicit; } - // Fast, allocation-free per-closed-generic cache. - return DefaultEventCache.HasDefaultEvent ? 3 : 0; + return DefaultEventCache.HasDefaultEvent ? BindingAffinity.DefaultEvent : 0; } /// @@ -65,7 +73,10 @@ public int GetAffinityForObject< /// [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] public IDisposable? BindCommandToObject< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T>( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T>( ICommand? command, T? target, IObservable commandParameter) @@ -73,20 +84,13 @@ public IDisposable? BindCommandToObject< { ArgumentExceptionHelper.ThrowIfNull(target); - // Typical binding semantics: null command => no-op binding. if (command is null) { - return Disposable.Empty; + return EmptyDisposable.Instance; } - var eventName = DefaultEventCache.DefaultEventName; - if (eventName is null) - { - throw new InvalidOperationException( + var eventName = DefaultEventCache.DefaultEventName ?? throw new InvalidOperationException( $"Couldn't find a default event to bind to on {typeof(T).FullName}, specify an event explicitly"); - } - - // Default events in this binder are EventArgs-shaped. return BindCommandToObject(command, target, commandParameter, eventName); } @@ -107,6 +111,10 @@ public IDisposable? BindCommandToObject< /// Thrown when is empty. /// [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IDisposable? BindCommandToObject( ICommand? command, T? target, @@ -124,37 +132,33 @@ public IDisposable? BindCommandToObject< if (command is null) { - return Disposable.Empty; + return EmptyDisposable.Instance; } - // Parameter value may be updated on a different thread than the event callback; - // ensure a consistent publication/read. object? latestParameter = null; - var ret = new CompositeDisposable(); + CompositeDisposable ret = []; - ret.Add(commandParameter.Subscribe(static x => - { - // Store under volatile semantics. - Volatile.Write(ref Unsafe.As(ref x), x); // no-op; keeps delegate static-friendly - })); + ret.Add(commandParameter.Subscribe(new DelegateObserver( + static x => + Volatile.Write(ref Unsafe.As(ref x), x)))); - // The above static trick is not useful because we still need to update latestParameter; keep a single closure, - // but use Volatile for correctness. ret.Clear(); - ret.Add(commandParameter.Subscribe(x => Volatile.Write(ref latestParameter, x))); + ret.Add(commandParameter.Subscribe(new DelegateObserver(x => Volatile.Write(ref latestParameter, x)))); - var evt = Observable.FromEventPattern(target, eventName); + var evt = new EventPatternObservable(target, eventName); - ret.Add(evt.Subscribe(_ => + ret.Add(evt.Subscribe(new DelegateObserver(_ => { var param = Volatile.Read(ref latestParameter); - if (command.CanExecute(param)) + if (!command.CanExecute(param)) { - command.Execute(param); + return; } - })); + + command.Execute(param); + }))); return ret; } @@ -173,7 +177,11 @@ public IDisposable? BindCommandToObject< /// /// Thrown when , , or is . /// - public IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T, TEventArgs>( + public IDisposable BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T, TEventArgs>( ICommand? command, T? target, IObservable commandParameter, @@ -186,34 +194,31 @@ public IDisposable? BindCommandToObject< ArgumentExceptionHelper.ThrowIfNull(addHandler); ArgumentExceptionHelper.ThrowIfNull(removeHandler); - // Preserve typical binding semantics: null command => no-op binding. if (command is null) { - return Disposable.Empty; + return EmptyDisposable.Instance; } - // latestParameter may be produced on a different thread than the UI event. object? latestParameter = null; - // Stable delegate for deterministic unsubscription. + var parameterSub = commandParameter.Subscribe(new DelegateObserver(x => Volatile.Write(ref latestParameter, x))); + + addHandler(Handler); + + return new CompositeDisposable( + parameterSub, + new ActionDisposable(() => removeHandler(Handler))); + void Handler(object? s, TEventArgs e) { var param = Volatile.Read(ref latestParameter); - if (command.CanExecute(param)) + if (!command.CanExecute(param)) { - command.Execute(param); + return; } - } - - // Subscribe to parameter changes first so the first event sees the latest parameter. - var parameterSub = commandParameter.Subscribe(x => Volatile.Write(ref latestParameter, x)); - - // Hook the event after parameter subscription; unhook deterministically on dispose. - addHandler(Handler); - return new CompositeDisposable( - parameterSub, - Disposable.Create(() => removeHandler(Handler))); + command.Execute(param); + } } /// @@ -243,29 +248,30 @@ public IDisposable BindCommandToObject( if (command is null) { - return Disposable.Empty; + return EmptyDisposable.Instance; } object? latestParameter = null; - // Stable delegate for deterministic unsubscription. + CompositeDisposable ret = + [ + commandParameter.Subscribe(new DelegateObserver(x => Volatile.Write(ref latestParameter, x))), + new ActionDisposable(() => removeHandler(Handler)) + ]; + + addHandler(Handler); + return ret; + void Handler(object? s, EventArgs e) { var param = Volatile.Read(ref latestParameter); - if (command.CanExecute(param)) + if (!command.CanExecute(param)) { - command.Execute(param); + return; } - } - var ret = new CompositeDisposable - { - commandParameter.Subscribe(x => Volatile.Write(ref latestParameter, x)), - Disposable.Create(() => removeHandler(Handler)) - }; - - addHandler(Handler); - return ret; + command.Execute(param); + } } /// @@ -273,7 +279,8 @@ void Handler(object? s, EventArgs e) /// /// The target type. private static class DefaultEventCache< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)]T> + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] + T> { /// /// Gets the selected default event name for , or if none exists. @@ -285,11 +292,14 @@ private static class DefaultEventCache< /// public static readonly bool HasDefaultEvent = DefaultEventName is not null; + /// + /// Scans the default events list and returns the name of the first event found on the type, or null if none exist. + /// + /// The name of the first supported default event, or null if none exist. private static string? FindDefaultEventName() { var type = typeof(T); - // Avoid LINQ allocations; scan in priority order. for (var i = 0; i < DefaultEventsToBind.Length; i++) { var name = DefaultEventsToBind[i].Name; diff --git a/src/ReactiveUI/Bindings/Command/ICommandBinderImplementation.cs b/src/ReactiveUI/Bindings/Command/ICommandBinderImplementation.cs index a44db9af3d..adbe26ffef 100644 --- a/src/ReactiveUI/Bindings/Command/ICommandBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Command/ICommandBinderImplementation.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; using System.Windows.Input; namespace ReactiveUI; @@ -39,17 +40,25 @@ internal interface ICommandBinderImplementation : IEnableLogger /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. /// It will remain active until disposed. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] - IReactiveBinding BindCommand( + IReactiveBinding BindCommand< + TView, + TViewModel, + TProp, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, + TParam>( TViewModel? viewModel, TView view, Expression> vmProperty, Expression> controlProperty, Expression> withParameter, string? toEvent = null) - where TView : class, IViewFor - where TViewModel : class - where TProp : ICommand - where TControl : class; + where TView : class, IViewFor + where TViewModel : class + where TProp : ICommand + where TControl : class; /// /// Binds a command from the view model to a control on the view, enabling the control to execute the command with a @@ -78,15 +87,23 @@ internal interface ICommandBinderImplementation : IEnableLogger /// An IReactiveBinding{TView, TProp} representing the established binding between the command and the control. /// It will remain active until disposed. [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] - IReactiveBinding BindCommand( + IReactiveBinding BindCommand< + TView, + TViewModel, + TProp, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + TControl, + TParam>( TViewModel? viewModel, TView view, Expression> vmProperty, Expression> controlProperty, IObservable withParameter, string? toEvent = null) - where TView : class, IViewFor - where TViewModel : class - where TProp : ICommand - where TControl : class; + where TView : class, IViewFor + where TViewModel : class + where TProp : ICommand + where TControl : class; } diff --git a/src/ReactiveUI/Bindings/Converter/BooleanToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/BooleanToStringTypeConverter.cs index b95ae5ab45..d725e5f7f9 100644 --- a/src/ReactiveUI/Bindings/Converter/BooleanToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/BooleanToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,7 +11,7 @@ namespace ReactiveUI; public sealed class BooleanToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(bool from, object? conversionHint, [NotNullWhen(true)] out string? result) diff --git a/src/ReactiveUI/Bindings/Converter/ByteToNullableByteTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/ByteToNullableByteTypeConverter.cs index 19b43fd968..77af19940b 100644 --- a/src/ReactiveUI/Bindings/Converter/ByteToNullableByteTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/ByteToNullableByteTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,7 +17,7 @@ public sealed class ByteToNullableByteTypeConverter : IBindingTypeConverter typeof(byte?); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvert(byte from, object? conversionHint, out byte? result) diff --git a/src/ReactiveUI/Bindings/Converter/ByteToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/ByteToStringTypeConverter.cs index d6e9f8e3f2..7317c43054 100644 --- a/src/ReactiveUI/Bindings/Converter/ByteToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/ByteToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,24 +11,30 @@ namespace ReactiveUI; public sealed class ByteToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(byte from, object? conversionHint, [NotNullWhen(true)] out string? result) { - if (conversionHint is int width) + switch (conversionHint) { - result = from.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.ToString(format); - return true; - } + case string format: + { + result = from.ToString(format); + return true; + } - result = from.ToString(); - return true; + default: + { + result = from.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/DateOnlyToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/DateOnlyToStringTypeConverter.cs index 8f3e8ec69c..1972f03da2 100644 --- a/src/ReactiveUI/Bindings/Converter/DateOnlyToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/DateOnlyToStringTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,7 +13,7 @@ namespace ReactiveUI; public sealed class DateOnlyToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(DateOnly from, object? conversionHint, [NotNullWhen(true)] out string? result) diff --git a/src/ReactiveUI/Bindings/Converter/DateTimeOffsetToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/DateTimeOffsetToStringTypeConverter.cs index a576c52965..c48a31ac29 100644 --- a/src/ReactiveUI/Bindings/Converter/DateTimeOffsetToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/DateTimeOffsetToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,7 +11,7 @@ namespace ReactiveUI; public sealed class DateTimeOffsetToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(DateTimeOffset from, object? conversionHint, [NotNullWhen(true)] out string? result) diff --git a/src/ReactiveUI/Bindings/Converter/DateTimeToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/DateTimeToStringTypeConverter.cs index 144bb8fd6e..84b8e2ff03 100644 --- a/src/ReactiveUI/Bindings/Converter/DateTimeToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/DateTimeToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,12 +11,12 @@ namespace ReactiveUI; public sealed class DateTimeToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(DateTime from, object? conversionHint, [NotNullWhen(true)] out string? result) { - result = from.ToString(); + result = from.ToString(System.Globalization.CultureInfo.CurrentCulture); return true; } } diff --git a/src/ReactiveUI/Bindings/Converter/DecimalToNullableDecimalTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/DecimalToNullableDecimalTypeConverter.cs index 26d12a4955..e314673392 100644 --- a/src/ReactiveUI/Bindings/Converter/DecimalToNullableDecimalTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/DecimalToNullableDecimalTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,7 +17,7 @@ public sealed class DecimalToNullableDecimalTypeConverter : IBindingTypeConverte public Type ToType => typeof(decimal?); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvert(decimal from, object? conversionHint, out decimal? result) diff --git a/src/ReactiveUI/Bindings/Converter/DecimalToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/DecimalToStringTypeConverter.cs index d3a66547af..d7e6a8deea 100644 --- a/src/ReactiveUI/Bindings/Converter/DecimalToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/DecimalToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,24 +11,30 @@ namespace ReactiveUI; public sealed class DecimalToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(decimal from, object? conversionHint, [NotNullWhen(true)] out string? result) { - if (conversionHint is int decimalPlaces) + switch (conversionHint) { - result = from.ToString($"F{decimalPlaces}"); - return true; - } + case int decimalPlaces: + { + result = from.ToString($"F{decimalPlaces}"); + return true; + } - if (conversionHint is string format) - { - result = from.ToString(format); - return true; - } + case string format: + { + result = from.ToString(format); + return true; + } - result = from.ToString(); - return true; + default: + { + result = from.ToString(System.Globalization.CultureInfo.CurrentCulture); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/DoubleToNullableDoubleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/DoubleToNullableDoubleTypeConverter.cs index b8b9f949f4..09e616cbd7 100644 --- a/src/ReactiveUI/Bindings/Converter/DoubleToNullableDoubleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/DoubleToNullableDoubleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,7 +17,7 @@ public sealed class DoubleToNullableDoubleTypeConverter : IBindingTypeConverter< public Type ToType => typeof(double?); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvert(double from, object? conversionHint, out double? result) diff --git a/src/ReactiveUI/Bindings/Converter/DoubleToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/DoubleToStringTypeConverter.cs index 1235df7f7b..29e66e47c6 100644 --- a/src/ReactiveUI/Bindings/Converter/DoubleToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/DoubleToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,24 +11,30 @@ namespace ReactiveUI; public sealed class DoubleToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(double from, object? conversionHint, [NotNullWhen(true)] out string? result) { - if (conversionHint is int decimalPlaces) + switch (conversionHint) { - result = from.ToString($"F{decimalPlaces}"); - return true; - } + case int decimalPlaces: + { + result = from.ToString($"F{decimalPlaces}"); + return true; + } - if (conversionHint is string format) - { - result = from.ToString(format); - return true; - } + case string format: + { + result = from.ToString(format); + return true; + } - result = from.ToString(); - return true; + default: + { + result = from.ToString(System.Globalization.CultureInfo.CurrentCulture); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs index a566e332f0..6d5207fbd1 100644 --- a/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/EqualityTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -32,7 +32,6 @@ public sealed class EqualityTypeConverter : IBindingTypeConverter /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - // Always return a bool result result = Equals(from, conversionHint); return true; } diff --git a/src/ReactiveUI/Bindings/Converter/GuidToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/GuidToStringTypeConverter.cs index 9d9d9aa17e..b1b3e0a432 100644 --- a/src/ReactiveUI/Bindings/Converter/GuidToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/GuidToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,7 +11,7 @@ namespace ReactiveUI; public sealed class GuidToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(Guid from, object? conversionHint, [NotNullWhen(true)] out string? result) diff --git a/src/ReactiveUI/Bindings/Converter/IntegerToNullableIntegerTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/IntegerToNullableIntegerTypeConverter.cs index ac9ba2695e..dd8071fb59 100644 --- a/src/ReactiveUI/Bindings/Converter/IntegerToNullableIntegerTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/IntegerToNullableIntegerTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,7 +17,7 @@ public sealed class IntegerToNullableIntegerTypeConverter : IBindingTypeConverte public Type ToType => typeof(int?); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvert(int from, object? conversionHint, out int? result) diff --git a/src/ReactiveUI/Bindings/Converter/IntegerToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/IntegerToStringTypeConverter.cs index 7793e9cf43..63944afaec 100644 --- a/src/ReactiveUI/Bindings/Converter/IntegerToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/IntegerToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,24 +11,30 @@ namespace ReactiveUI; public sealed class IntegerToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(int from, object? conversionHint, [NotNullWhen(true)] out string? result) { - if (conversionHint is int width) + switch (conversionHint) { - result = from.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.ToString(format); - return true; - } + case string format: + { + result = from.ToString(format); + return true; + } - result = from.ToString(); - return true; + default: + { + result = from.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/LongToNullableLongTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/LongToNullableLongTypeConverter.cs index c0d6b0936e..0c26f8f042 100644 --- a/src/ReactiveUI/Bindings/Converter/LongToNullableLongTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/LongToNullableLongTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,7 +17,7 @@ public sealed class LongToNullableLongTypeConverter : IBindingTypeConverter typeof(long?); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvert(long from, object? conversionHint, out long? result) diff --git a/src/ReactiveUI/Bindings/Converter/LongToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/LongToStringTypeConverter.cs index 437cfad389..7efbbf233e 100644 --- a/src/ReactiveUI/Bindings/Converter/LongToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/LongToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,24 +11,30 @@ namespace ReactiveUI; public sealed class LongToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(long from, object? conversionHint, [NotNullWhen(true)] out string? result) { - if (conversionHint is int width) + switch (conversionHint) { - result = from.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.ToString(format); - return true; - } + case string format: + { + result = from.ToString(format); + return true; + } - result = from.ToString(); - return true; + default: + { + result = from.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableBooleanToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableBooleanToStringTypeConverter.cs index 0cd174c2a5..e7ab29714e 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableBooleanToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableBooleanToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableBooleanToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(bool? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(bool? from, object? conversionHint, out string? result) { if (!from.HasValue) { diff --git a/src/ReactiveUI/Bindings/Converter/NullableByteToByteTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableByteToByteTypeConverter.cs index 32dff36c13..33d5d3a777 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableByteToByteTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableByteToByteTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,14 +20,14 @@ public sealed class NullableByteToByteTypeConverter : IBindingTypeConverter typeof(byte); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public bool TryConvert(byte? from, object? conversionHint, [NotNullWhen(true)] out byte result) + public bool TryConvert(byte? from, object? conversionHint, out byte result) { if (from is null) { - result = default; + result = 0; return false; } @@ -38,20 +38,25 @@ public bool TryConvert(byte? from, object? conversionHint, [NotNullWhen(true)] o /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - if (from is null) + switch (from) { - result = null; - return TryConvert(null, conversionHint, out _); + case null: + { + result = null; + return TryConvert(null, conversionHint, out _); + } + + case byte value when TryConvert(value, conversionHint, out var typedResult): + { + result = typedResult; + return true; + } + + default: + { + result = null; + return false; + } } - - if (from is byte value) - { - return TryConvert(value, conversionHint, out var typedResult) - ? (result = typedResult) is not null - : (result = default) is null && false; - } - - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableByteToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableByteToStringTypeConverter.cs index 4ac149aea6..c973af2cec 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableByteToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableByteToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableByteToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(byte? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(byte? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,19 +22,25 @@ public override bool TryConvert(byte? from, object? conversionHint, [MaybeNullWh return true; } - if (conversionHint is int width) + switch (conversionHint) { - result = from.Value.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.Value.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.Value.ToString(format); - return true; - } + case string format: + { + result = from.Value.ToString(format); + return true; + } - result = from.Value.ToString(); - return true; + default: + { + result = from.Value.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableDateOnlyToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableDateOnlyToStringTypeConverter.cs index 250d340837..5b9f612617 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableDateOnlyToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableDateOnlyToStringTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,10 +13,10 @@ namespace ReactiveUI; public sealed class NullableDateOnlyToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(DateOnly? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(DateOnly? from, object? conversionHint, out string? result) { if (!from.HasValue) { diff --git a/src/ReactiveUI/Bindings/Converter/NullableDateTimeOffsetToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableDateTimeOffsetToStringTypeConverter.cs index 4570049377..a5a191a2ce 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableDateTimeOffsetToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableDateTimeOffsetToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,13 @@ namespace ReactiveUI; public sealed class NullableDateTimeOffsetToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(DateTimeOffset? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert( + DateTimeOffset? from, + object? conversionHint, + out string? result) { if (!from.HasValue) { diff --git a/src/ReactiveUI/Bindings/Converter/NullableDateTimeToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableDateTimeToStringTypeConverter.cs index a935c9f8bb..5706dab84b 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableDateTimeToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableDateTimeToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableDateTimeToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(DateTime? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(DateTime? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,7 +22,7 @@ public override bool TryConvert(DateTime? from, object? conversionHint, [MaybeNu return true; } - result = from.Value.ToString(); + result = from.Value.ToString(System.Globalization.CultureInfo.CurrentCulture); return true; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableDecimalToDecimalTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableDecimalToDecimalTypeConverter.cs index b27251b35e..103064f42f 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableDecimalToDecimalTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableDecimalToDecimalTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,14 +20,14 @@ public sealed class NullableDecimalToDecimalTypeConverter : IBindingTypeConverte public Type ToType => typeof(decimal); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public bool TryConvert(decimal? from, object? conversionHint, [NotNullWhen(true)] out decimal result) + public bool TryConvert(decimal? from, object? conversionHint, out decimal result) { if (from is null) { - result = default; + result = 0; return false; } @@ -38,20 +38,25 @@ public bool TryConvert(decimal? from, object? conversionHint, [NotNullWhen(true) /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - if (from is null) + switch (from) { - result = null; - return TryConvert(null, conversionHint, out _); + case null: + { + result = null; + return TryConvert(null, conversionHint, out _); + } + + case decimal value when TryConvert(value, conversionHint, out var typedResult): + { + result = typedResult; + return true; + } + + default: + { + result = null; + return false; + } } - - if (from is decimal value) - { - return TryConvert(value, conversionHint, out var typedResult) - ? (result = typedResult) is not null - : (result = default) is null && false; - } - - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableDecimalToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableDecimalToStringTypeConverter.cs index 41ca71e863..dc8d5c30a7 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableDecimalToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableDecimalToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableDecimalToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(decimal? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(decimal? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,19 +22,25 @@ public override bool TryConvert(decimal? from, object? conversionHint, [MaybeNul return true; } - if (conversionHint is int decimalPlaces) + switch (conversionHint) { - result = from.Value.ToString($"F{decimalPlaces}"); - return true; - } + case int decimalPlaces: + { + result = from.Value.ToString($"F{decimalPlaces}"); + return true; + } - if (conversionHint is string format) - { - result = from.Value.ToString(format); - return true; - } + case string format: + { + result = from.Value.ToString(format); + return true; + } - result = from.Value.ToString(); - return true; + default: + { + result = from.Value.ToString(System.Globalization.CultureInfo.CurrentCulture); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableDoubleToDoubleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableDoubleToDoubleTypeConverter.cs index dd9d26cf02..8ee916ba03 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableDoubleToDoubleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableDoubleToDoubleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,14 +20,14 @@ public sealed class NullableDoubleToDoubleTypeConverter : IBindingTypeConverter< public Type ToType => typeof(double); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public bool TryConvert(double? from, object? conversionHint, [NotNullWhen(true)] out double result) + public bool TryConvert(double? from, object? conversionHint, out double result) { if (from is null) { - result = default; + result = 0; return false; } @@ -38,22 +38,25 @@ public bool TryConvert(double? from, object? conversionHint, [NotNullWhen(true)] /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - // Handle null by returning false - if (from is null) + switch (from) { - result = null; - return TryConvert(null, conversionHint, out _); + case null: + { + result = null; + return TryConvert(null, conversionHint, out _); + } + + case double value when TryConvert(value, conversionHint, out var typedResult): + { + result = typedResult; + return true; + } + + default: + { + result = null; + return false; + } } - - // Handle double by converting through strongly-typed method - if (from is double value) - { - return TryConvert(value, conversionHint, out var typedResult) - ? (result = typedResult) is not null - : (result = default) is null && false; - } - - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableDoubleToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableDoubleToStringTypeConverter.cs index 7f357c205f..676a8b2847 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableDoubleToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableDoubleToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableDoubleToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(double? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(double? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,19 +22,25 @@ public override bool TryConvert(double? from, object? conversionHint, [MaybeNull return true; } - if (conversionHint is int decimalPlaces) + switch (conversionHint) { - result = from.Value.ToString($"F{decimalPlaces}"); - return true; - } + case int decimalPlaces: + { + result = from.Value.ToString($"F{decimalPlaces}"); + return true; + } - if (conversionHint is string format) - { - result = from.Value.ToString(format); - return true; - } + case string format: + { + result = from.Value.ToString(format); + return true; + } - result = from.Value.ToString(); - return true; + default: + { + result = from.Value.ToString(System.Globalization.CultureInfo.CurrentCulture); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableGuidToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableGuidToStringTypeConverter.cs index abd65b4c7c..471f607e92 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableGuidToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableGuidToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableGuidToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(Guid? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(Guid? from, object? conversionHint, out string? result) { if (!from.HasValue) { diff --git a/src/ReactiveUI/Bindings/Converter/NullableIntegerToIntegerTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableIntegerToIntegerTypeConverter.cs index 9a2eb02125..4179ceed3c 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableIntegerToIntegerTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableIntegerToIntegerTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,14 +20,14 @@ public sealed class NullableIntegerToIntegerTypeConverter : IBindingTypeConverte public Type ToType => typeof(int); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public bool TryConvert(int? from, object? conversionHint, [NotNullWhen(true)] out int result) + public bool TryConvert(int? from, object? conversionHint, out int result) { if (from is null) { - result = default; + result = 0; return false; } @@ -38,20 +38,25 @@ public bool TryConvert(int? from, object? conversionHint, [NotNullWhen(true)] ou /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - if (from is null) + switch (from) { - result = null; - return TryConvert(null, conversionHint, out _); + case null: + { + result = null; + return TryConvert(null, conversionHint, out _); + } + + case int value when TryConvert(value, conversionHint, out var typedResult): + { + result = typedResult; + return true; + } + + default: + { + result = null; + return false; + } } - - if (from is int value) - { - return TryConvert(value, conversionHint, out var typedResult) - ? (result = typedResult) is not null - : (result = default) is null && false; - } - - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableIntegerToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableIntegerToStringTypeConverter.cs index 4da4eae06d..5b27dc5100 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableIntegerToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableIntegerToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableIntegerToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(int? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(int? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,19 +22,25 @@ public override bool TryConvert(int? from, object? conversionHint, [MaybeNullWhe return true; } - if (conversionHint is int width) + switch (conversionHint) { - result = from.Value.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.Value.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.Value.ToString(format); - return true; - } + case string format: + { + result = from.Value.ToString(format); + return true; + } - result = from.Value.ToString(); - return true; + default: + { + result = from.Value.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableLongToLongTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableLongToLongTypeConverter.cs index 67682d1451..af5cdfc1c7 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableLongToLongTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableLongToLongTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,14 +20,14 @@ public sealed class NullableLongToLongTypeConverter : IBindingTypeConverter typeof(long); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public bool TryConvert(long? from, object? conversionHint, [NotNullWhen(true)] out long result) + public bool TryConvert(long? from, object? conversionHint, out long result) { if (from is null) { - result = default; + result = 0; return false; } @@ -38,20 +38,25 @@ public bool TryConvert(long? from, object? conversionHint, [NotNullWhen(true)] o /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - if (from is null) + switch (from) { - result = null; - return TryConvert(null, conversionHint, out _); + case null: + { + result = null; + return TryConvert(null, conversionHint, out _); + } + + case long value when TryConvert(value, conversionHint, out var typedResult): + { + result = typedResult; + return true; + } + + default: + { + result = null; + return false; + } } - - if (from is long value) - { - return TryConvert(value, conversionHint, out var typedResult) - ? (result = typedResult) is not null - : (result = default) is null && false; - } - - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableLongToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableLongToStringTypeConverter.cs index 8269cd6e88..0ea00b9f60 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableLongToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableLongToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableLongToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(long? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(long? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,19 +22,25 @@ public override bool TryConvert(long? from, object? conversionHint, [MaybeNullWh return true; } - if (conversionHint is int width) + switch (conversionHint) { - result = from.Value.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.Value.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.Value.ToString(format); - return true; - } + case string format: + { + result = from.Value.ToString(format); + return true; + } - result = from.Value.ToString(); - return true; + default: + { + result = from.Value.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableShortToShortTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableShortToShortTypeConverter.cs index ccbb87ada9..7ca3c2b0c3 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableShortToShortTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableShortToShortTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,14 +20,14 @@ public sealed class NullableShortToShortTypeConverter : IBindingTypeConverter typeof(short); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public bool TryConvert(short? from, object? conversionHint, [NotNullWhen(true)] out short result) + public bool TryConvert(short? from, object? conversionHint, out short result) { if (from is null) { - result = default; + result = 0; return false; } @@ -38,20 +38,25 @@ public bool TryConvert(short? from, object? conversionHint, [NotNullWhen(true)] /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - if (from is null) + switch (from) { - result = null; - return TryConvert(null, conversionHint, out _); + case null: + { + result = null; + return TryConvert(null, conversionHint, out _); + } + + case short value when TryConvert(value, conversionHint, out var typedResult): + { + result = typedResult; + return true; + } + + default: + { + result = null; + return false; + } } - - if (from is short value) - { - return TryConvert(value, conversionHint, out var typedResult) - ? (result = typedResult) is not null - : (result = default) is null && false; - } - - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableShortToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableShortToStringTypeConverter.cs index 5f69c35e7b..0bbd7a2181 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableShortToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableShortToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableShortToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(short? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(short? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,19 +22,25 @@ public override bool TryConvert(short? from, object? conversionHint, [MaybeNullW return true; } - if (conversionHint is int width) + switch (conversionHint) { - result = from.Value.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.Value.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.Value.ToString(format); - return true; - } + case string format: + { + result = from.Value.ToString(format); + return true; + } - result = from.Value.ToString(); - return true; + default: + { + result = from.Value.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableSingleToSingleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableSingleToSingleTypeConverter.cs index 64684c7a67..62670ac01b 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableSingleToSingleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableSingleToSingleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,14 +20,14 @@ public sealed class NullableSingleToSingleTypeConverter : IBindingTypeConverter< public Type ToType => typeof(float); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public bool TryConvert(float? from, object? conversionHint, [NotNullWhen(true)] out float result) + public bool TryConvert(float? from, object? conversionHint, out float result) { if (from is null) { - result = default; + result = 0; return false; } @@ -38,20 +38,25 @@ public bool TryConvert(float? from, object? conversionHint, [NotNullWhen(true)] /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - if (from is null) + switch (from) { - result = null; - return TryConvert(null, conversionHint, out _); + case null: + { + result = null; + return TryConvert(null, conversionHint, out _); + } + + case float value when TryConvert(value, conversionHint, out var typedResult): + { + result = typedResult; + return true; + } + + default: + { + result = null; + return false; + } } - - if (from is float value) - { - return TryConvert(value, conversionHint, out var typedResult) - ? (result = typedResult) is not null - : (result = default) is null && false; - } - - result = null; - return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableSingleToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableSingleToStringTypeConverter.cs index c4ed36dca0..8a0ae695e5 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableSingleToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableSingleToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableSingleToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(float? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(float? from, object? conversionHint, out string? result) { if (!from.HasValue) { @@ -22,19 +22,25 @@ public override bool TryConvert(float? from, object? conversionHint, [MaybeNullW return true; } - if (conversionHint is int decimalPlaces) + switch (conversionHint) { - result = from.Value.ToString($"F{decimalPlaces}"); - return true; - } + case int decimalPlaces: + { + result = from.Value.ToString($"F{decimalPlaces}"); + return true; + } - if (conversionHint is string format) - { - result = from.Value.ToString(format); - return true; - } + case string format: + { + result = from.Value.ToString(format); + return true; + } - result = from.Value.ToString(); - return true; + default: + { + result = from.Value.ToString(System.Globalization.CultureInfo.CurrentCulture); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/NullableTimeOnlyToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableTimeOnlyToStringTypeConverter.cs index 3758ea88cd..074f77c2dc 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableTimeOnlyToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableTimeOnlyToStringTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,10 +13,10 @@ namespace ReactiveUI; public sealed class NullableTimeOnlyToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(TimeOnly? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(TimeOnly? from, object? conversionHint, out string? result) { if (!from.HasValue) { diff --git a/src/ReactiveUI/Bindings/Converter/NullableTimeSpanToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/NullableTimeSpanToStringTypeConverter.cs index 55e5081a0e..9671fffdf1 100644 --- a/src/ReactiveUI/Bindings/Converter/NullableTimeSpanToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/NullableTimeSpanToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class NullableTimeSpanToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(TimeSpan? from, object? conversionHint, [MaybeNullWhen(true)] out string? result) + public override bool TryConvert(TimeSpan? from, object? conversionHint, out string? result) { if (!from.HasValue) { diff --git a/src/ReactiveUI/Bindings/Converter/ShortToNullableShortTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/ShortToNullableShortTypeConverter.cs index f2c746f2e4..2d7db44072 100644 --- a/src/ReactiveUI/Bindings/Converter/ShortToNullableShortTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/ShortToNullableShortTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,7 +17,7 @@ public sealed class ShortToNullableShortTypeConverter : IBindingTypeConverter typeof(short?); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvert(short from, object? conversionHint, out short? result) diff --git a/src/ReactiveUI/Bindings/Converter/ShortToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/ShortToStringTypeConverter.cs index 621e1e8175..064dc407fc 100644 --- a/src/ReactiveUI/Bindings/Converter/ShortToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/ShortToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,24 +11,30 @@ namespace ReactiveUI; public sealed class ShortToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(short from, object? conversionHint, [NotNullWhen(true)] out string? result) { - if (conversionHint is int width) + switch (conversionHint) { - result = from.ToString($"D{width}"); - return true; - } + case int width: + { + result = from.ToString($"D{width}"); + return true; + } - if (conversionHint is string format) - { - result = from.ToString(format); - return true; - } + case string format: + { + result = from.ToString(format); + return true; + } - result = from.ToString(); - return true; + default: + { + result = from.ToString(); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/SingleToNullableSingleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/SingleToNullableSingleTypeConverter.cs index 70b707ef36..49cb543790 100644 --- a/src/ReactiveUI/Bindings/Converter/SingleToNullableSingleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/SingleToNullableSingleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,7 +17,7 @@ public sealed class SingleToNullableSingleTypeConverter : IBindingTypeConverter< public Type ToType => typeof(float?); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvert(float from, object? conversionHint, out float? result) diff --git a/src/ReactiveUI/Bindings/Converter/SingleToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/SingleToStringTypeConverter.cs index 8b5874f82c..6a0b2acd25 100644 --- a/src/ReactiveUI/Bindings/Converter/SingleToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/SingleToStringTypeConverter.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Globalization; + namespace ReactiveUI; /// @@ -11,24 +13,30 @@ namespace ReactiveUI; public sealed class SingleToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(float from, object? conversionHint, [NotNullWhen(true)] out string? result) { - if (conversionHint is int decimalPlaces) + switch (conversionHint) { - result = from.ToString($"F{decimalPlaces}"); - return true; - } + case int decimalPlaces: + { + result = from.ToString($"F{decimalPlaces}"); + return true; + } - if (conversionHint is string format) - { - result = from.ToString(format); - return true; - } + case string format: + { + result = from.ToString(format); + return true; + } - result = from.ToString(); - return true; + default: + { + result = from.ToString(CultureInfo.InvariantCulture); + return true; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/StringConverter.cs b/src/ReactiveUI/Bindings/Converter/StringConverter.cs index d21db93ae9..867dcccfca 100644 --- a/src/ReactiveUI/Bindings/Converter/StringConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -21,24 +21,30 @@ public sealed class StringConverter : IBindingTypeConverter public Type ToType => typeof(string); /// - public int GetAffinityForObjects() => 2; + public int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public bool TryConvertTyped(object? from, object? conversionHint, [NotNullWhen(true)] out object? result) { - if (from is null) + switch (from) { - result = null; - return false; - } + case null: + { + result = null; + return false; + } - if (from is string s) - { - result = s; - return true; - } + case string s: + { + result = s; + return true; + } - result = null; - return false; + default: + { + result = null; + return false; + } + } } } diff --git a/src/ReactiveUI/Bindings/Converter/StringToBooleanTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToBooleanTypeConverter.cs index dd75d5d947..cc0ed27235 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToBooleanTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToBooleanTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToBooleanTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out bool result) + public override bool TryConvert(string? from, object? conversionHint, out bool result) { if (from is null) { - result = default; + result = false; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToByteTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToByteTypeConverter.cs index f4c02cd2b2..9421c6cb92 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToByteTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToByteTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToByteTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out byte result) + public override bool TryConvert(string? from, object? conversionHint, out byte result) { if (from is null) { - result = default; + result = 0; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToDateOnlyTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToDateOnlyTypeConverter.cs index 3bbe7ff51c..b2d10d6a45 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToDateOnlyTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToDateOnlyTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,10 +13,10 @@ namespace ReactiveUI; public sealed class StringToDateOnlyTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out DateOnly result) + public override bool TryConvert(string? from, object? conversionHint, out DateOnly result) { if (from is null) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToDateTimeOffsetTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToDateTimeOffsetTypeConverter.cs index 6e8ba75d8a..dff6c5b0a9 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToDateTimeOffsetTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToDateTimeOffsetTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToDateTimeOffsetTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out DateTimeOffset result) + public override bool TryConvert(string? from, object? conversionHint, out DateTimeOffset result) { if (from is null) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToDateTimeTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToDateTimeTypeConverter.cs index b2b9b9bc5f..97148e5409 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToDateTimeTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToDateTimeTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToDateTimeTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out DateTime result) + public override bool TryConvert(string? from, object? conversionHint, out DateTime result) { if (from is null) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToDecimalTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToDecimalTypeConverter.cs index 4bcb78a44e..50380b896f 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToDecimalTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToDecimalTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToDecimalTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out decimal result) + public override bool TryConvert(string? from, object? conversionHint, out decimal result) { if (from is null) { - result = default; + result = 0; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToDoubleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToDoubleTypeConverter.cs index 44e9c492c1..3edbac3899 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToDoubleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToDoubleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToDoubleTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out double result) + public override bool TryConvert(string? from, object? conversionHint, out double result) { if (from is null) { - result = default; + result = 0; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToGuidTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToGuidTypeConverter.cs index 5d117cc153..459106b8db 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToGuidTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToGuidTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,17 +11,17 @@ namespace ReactiveUI; public sealed class StringToGuidTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out Guid result) + public override bool TryConvert(string? from, object? conversionHint, out Guid result) { - if (from is null) + if (from is not null) { - result = default; - return false; + return Guid.TryParse(from, out result); } - return Guid.TryParse(from, out result); + result = Guid.Empty; + return false; } } diff --git a/src/ReactiveUI/Bindings/Converter/StringToIntegerTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToIntegerTypeConverter.cs index 8281551efa..061e7fc679 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToIntegerTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToIntegerTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToIntegerTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out int result) + public override bool TryConvert(string? from, object? conversionHint, out int result) { if (from is null) { - result = default; + result = 0; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToLongTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToLongTypeConverter.cs index 27957998cc..b46c3e03d0 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToLongTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToLongTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToLongTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out long result) + public override bool TryConvert(string? from, object? conversionHint, out long result) { if (from is null) { - result = default; + result = 0; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableBooleanTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableBooleanTypeConverter.cs index 560d44c428..24261ba19b 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableBooleanTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableBooleanTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableBooleanTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out bool? result) + public override bool TryConvert(string? from, object? conversionHint, out bool? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableByteTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableByteTypeConverter.cs index fc9d4ec6ea..cca8280e88 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableByteTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableByteTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableByteTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out byte? result) + public override bool TryConvert(string? from, object? conversionHint, out byte? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableDateOnlyTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableDateOnlyTypeConverter.cs index 58862c62eb..690a189cfe 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableDateOnlyTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableDateOnlyTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,10 +13,10 @@ namespace ReactiveUI; public sealed class StringToNullableDateOnlyTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out DateOnly? result) + public override bool TryConvert(string? from, object? conversionHint, out DateOnly? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeOffsetTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeOffsetTypeConverter.cs index 3b6109524f..fb6f1c3ffc 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeOffsetTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeOffsetTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,13 @@ namespace ReactiveUI; public sealed class StringToNullableDateTimeOffsetTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out DateTimeOffset? result) + public override bool TryConvert( + string? from, + object? conversionHint, + out DateTimeOffset? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeTypeConverter.cs index 713c9ee1a8..44015a8785 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableDateTimeTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableDateTimeTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out DateTime? result) + public override bool TryConvert(string? from, object? conversionHint, out DateTime? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableDecimalTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableDecimalTypeConverter.cs index b760323455..94777f7d33 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableDecimalTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableDecimalTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableDecimalTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out decimal? result) + public override bool TryConvert(string? from, object? conversionHint, out decimal? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableDoubleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableDoubleTypeConverter.cs index 834dce75b1..e9490785a9 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableDoubleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableDoubleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableDoubleTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out double? result) + public override bool TryConvert(string? from, object? conversionHint, out double? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableGuidTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableGuidTypeConverter.cs index db6abfe100..1a51a7684e 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableGuidTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableGuidTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableGuidTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out Guid? result) + public override bool TryConvert(string? from, object? conversionHint, out Guid? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableIntegerTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableIntegerTypeConverter.cs index 0b9e36e9f0..a9cb168372 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableIntegerTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableIntegerTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableIntegerTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out int? result) + public override bool TryConvert(string? from, object? conversionHint, out int? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableLongTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableLongTypeConverter.cs index 2bbbff3f32..1e3c843432 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableLongTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableLongTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableLongTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out long? result) + public override bool TryConvert(string? from, object? conversionHint, out long? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableShortTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableShortTypeConverter.cs index 39104a3b60..85e519945c 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableShortTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableShortTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableShortTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out short? result) + public override bool TryConvert(string? from, object? conversionHint, out short? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableSingleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableSingleTypeConverter.cs index 91cf65ae86..2df2c2d786 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableSingleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableSingleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableSingleTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out float? result) + public override bool TryConvert(string? from, object? conversionHint, out float? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableTimeOnlyTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableTimeOnlyTypeConverter.cs index ac0f877196..10292bb84d 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableTimeOnlyTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableTimeOnlyTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,10 +13,10 @@ namespace ReactiveUI; public sealed class StringToNullableTimeOnlyTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out TimeOnly? result) + public override bool TryConvert(string? from, object? conversionHint, out TimeOnly? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToNullableTimeSpanTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToNullableTimeSpanTypeConverter.cs index 50bc70522d..d8737c30cb 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToNullableTimeSpanTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToNullableTimeSpanTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,10 +11,10 @@ namespace ReactiveUI; public sealed class StringToNullableTimeSpanTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [MaybeNullWhen(true)] out TimeSpan? result) + public override bool TryConvert(string? from, object? conversionHint, out TimeSpan? result) { if (string.IsNullOrEmpty(from)) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToShortTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToShortTypeConverter.cs index ccd9c5e217..751876a6b9 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToShortTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToShortTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToShortTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out short result) + public override bool TryConvert(string? from, object? conversionHint, out short result) { if (from is null) { - result = default; + result = 0; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToSingleTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToSingleTypeConverter.cs index 5b22823d94..0def8076ae 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToSingleTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToSingleTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToSingleTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out float result) + public override bool TryConvert(string? from, object? conversionHint, out float result) { if (from is null) { - result = default; + result = 0; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToTimeOnlyTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToTimeOnlyTypeConverter.cs index 6ec937cacc..2733d8f538 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToTimeOnlyTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToTimeOnlyTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,10 +13,10 @@ namespace ReactiveUI; public sealed class StringToTimeOnlyTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out TimeOnly result) + public override bool TryConvert(string? from, object? conversionHint, out TimeOnly result) { if (from is null) { diff --git a/src/ReactiveUI/Bindings/Converter/StringToTimeSpanTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToTimeSpanTypeConverter.cs index 4f87e48652..3d94cab202 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToTimeSpanTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToTimeSpanTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,14 +11,14 @@ namespace ReactiveUI; public sealed class StringToTimeSpanTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// - public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out TimeSpan result) + public override bool TryConvert(string? from, object? conversionHint, out TimeSpan result) { if (from is null) { - result = default; + result = TimeSpan.Zero; return false; } diff --git a/src/ReactiveUI/Bindings/Converter/StringToUriTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/StringToUriTypeConverter.cs index 5a7fe4b19e..c78da139c9 100644 --- a/src/ReactiveUI/Bindings/Converter/StringToUriTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/StringToUriTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,7 +11,7 @@ namespace ReactiveUI; public sealed class StringToUriTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(string? from, object? conversionHint, [NotNullWhen(true)] out Uri? result) diff --git a/src/ReactiveUI/Bindings/Converter/TimeOnlyToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/TimeOnlyToStringTypeConverter.cs index 878db2c26b..e14d2d348b 100644 --- a/src/ReactiveUI/Bindings/Converter/TimeOnlyToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/TimeOnlyToStringTypeConverter.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; namespace ReactiveUI; @@ -14,7 +13,7 @@ namespace ReactiveUI; public sealed class TimeOnlyToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(TimeOnly from, object? conversionHint, [NotNullWhen(true)] out string? result) diff --git a/src/ReactiveUI/Bindings/Converter/TimeSpanToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/TimeSpanToStringTypeConverter.cs index deb286f40f..b66ff3c808 100644 --- a/src/ReactiveUI/Bindings/Converter/TimeSpanToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/TimeSpanToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,7 +11,7 @@ namespace ReactiveUI; public sealed class TimeSpanToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(TimeSpan from, object? conversionHint, [NotNullWhen(true)] out string? result) diff --git a/src/ReactiveUI/Bindings/Converter/UriToStringTypeConverter.cs b/src/ReactiveUI/Bindings/Converter/UriToStringTypeConverter.cs index 079dd9e928..36f55352cb 100644 --- a/src/ReactiveUI/Bindings/Converter/UriToStringTypeConverter.cs +++ b/src/ReactiveUI/Bindings/Converter/UriToStringTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,7 +11,7 @@ namespace ReactiveUI; public sealed class UriToStringTypeConverter : BindingTypeConverter { /// - public override int GetAffinityForObjects() => 2; + public override int GetAffinityForObjects() => BindingAffinity.DefaultInternalTypeConverter; /// public override bool TryConvert(Uri? from, object? conversionHint, [NotNullWhen(true)] out string? result) diff --git a/src/ReactiveUI/Bindings/Converters/BindingFallbackConverterRegistry.cs b/src/ReactiveUI/Bindings/Converters/BindingFallbackConverterRegistry.cs index 3cc63bd6b4..d090b30a28 100644 --- a/src/ReactiveUI/Bindings/Converters/BindingFallbackConverterRegistry.cs +++ b/src/ReactiveUI/Bindings/Converters/BindingFallbackConverterRegistry.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -79,13 +79,11 @@ public void Register(IBindingFallbackConverter converter) lock (_gate) { - var snap = _snapshot ?? new Snapshot(new List(8)); + var snap = _snapshot ?? new Snapshot(new(8)); - // Copy-on-write update: clone the list - var newList = new List(snap.Converters) { converter }; + List newList = [..snap.Converters, converter]; - // Publish the new snapshot (atomic via reference assignment) - _snapshot = new Snapshot(newList); + _snapshot = new(newList); } } @@ -112,8 +110,10 @@ public void Register(IBindingFallbackConverter converter) /// /// public IBindingFallbackConverter? TryGetConverter( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type toType) { ArgumentExceptionHelper.ThrowIfNull(fromType); ArgumentExceptionHelper.ThrowIfNull(toType); @@ -124,7 +124,6 @@ public void Register(IBindingFallbackConverter converter) return null; } - // Find the converter with the highest affinity IBindingFallbackConverter? best = null; var bestScore = -1; @@ -162,7 +161,6 @@ public IEnumerable GetAllConverters() return []; } - // Return a copy to avoid exposing internal list return [.. snap.Converters]; } diff --git a/src/ReactiveUI/Bindings/Converters/BindingTypeConverterRegistry.cs b/src/ReactiveUI/Bindings/Converters/BindingTypeConverterRegistry.cs index a20f3474ce..ca56df4e4d 100644 --- a/src/ReactiveUI/Bindings/Converters/BindingTypeConverterRegistry.cs +++ b/src/ReactiveUI/Bindings/Converters/BindingTypeConverterRegistry.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -35,6 +35,9 @@ namespace ReactiveUI; /// public sealed class BindingTypeConverterRegistry { + /// The initial capacity used for a per-type-pair converter list. + private const int DefaultConverterListCapacity = 4; + #if NET9_0_OR_GREATER /// /// Synchronization primitive guarding mutations to the registry's internal state. @@ -87,26 +90,16 @@ public void Register(IBindingTypeConverter converter) lock (_gate) { - var snap = _snapshot ?? new Snapshot(new Dictionary<(Type fromType, Type toType), List>(16)); + var snap = _snapshot ?? new Snapshot(new(16)); - // Copy-on-write update: clone the dictionary shallowly var newDict = CloneRegistryShallow(snap.ConvertersByTypePair); - if (!newDict.TryGetValue(key, out var list)) - { - list = new List(4); - } - else - { - // Copy-on-write at the list level: clone before mutating - list = [.. list]; - } + List outputList = !newDict.TryGetValue(key, out var list) ? new(DefaultConverterListCapacity) : [.. list]; - list.Add(converter); - newDict[key] = list; + outputList.Add(converter); + newDict[key] = outputList; - // Publish the new snapshot (atomic via reference assignment) - _snapshot = new Snapshot(newDict); + _snapshot = new(newDict); } } @@ -148,7 +141,6 @@ public void Register(IBindingTypeConverter converter) return null; } - // Find the converter with the highest affinity IBindingTypeConverter? best = null; var bestScore = -1; @@ -156,11 +148,13 @@ public void Register(IBindingTypeConverter converter) { var converter = list[i]; var score = converter.GetAffinityForObjects(); - if (score > bestScore && score > 0) + if (score <= bestScore || score <= 0) { - bestScore = score; - best = converter; + continue; } + + bestScore = score; + best = converter; } return best; @@ -185,9 +179,7 @@ public IEnumerable GetAllConverters() return []; } - // Flatten all lists into a single enumerable - // Use a list to avoid lazy evaluation issues with concurrent modifications - var result = new List(); + List result = []; foreach (var kvp in snap.ConvertersByTypePair) { result.AddRange(kvp.Value); @@ -213,7 +205,7 @@ public IEnumerable GetAllConverters() { ArgumentExceptionHelper.ThrowIfNull(source); - var clone = new Dictionary<(Type fromType, Type toType), List>(source.Count); + Dictionary<(Type fromType, Type toType), List> clone = new(source.Count); foreach (var kvp in source) { clone[kvp.Key] = kvp.Value; diff --git a/src/ReactiveUI/Bindings/Converters/ConverterMigrationHelper.cs b/src/ReactiveUI/Bindings/Converters/ConverterMigrationHelper.cs index 92bc9664a3..806510f3b8 100644 --- a/src/ReactiveUI/Bindings/Converters/ConverterMigrationHelper.cs +++ b/src/ReactiveUI/Bindings/Converters/ConverterMigrationHelper.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -111,18 +111,18 @@ public static ( IList TypedConverters, IList FallbackConverters, IList SetMethodConverters) - ExtractConverters(IReadonlyDependencyResolver resolver) + ExtractConverters(IReadonlyDependencyResolver resolver) { ArgumentExceptionHelper.ThrowIfNull(resolver); - var typed = new List( - resolver.GetServices().Where(static c => c is not null)!); + List typed = + [.. resolver.GetServices().Where(static c => c is not null)]; - var fallback = new List( - resolver.GetServices().Where(static c => c is not null)!); + List fallback = + [.. resolver.GetServices().Where(static c => c is not null)]; - var setMethod = new List( - resolver.GetServices().Where(static c => c is not null)!); + List setMethod = + [.. resolver.GetServices().Where(static c => c is not null)]; return (typed, fallback, setMethod); } diff --git a/src/ReactiveUI/Bindings/Converters/ConverterService.cs b/src/ReactiveUI/Bindings/Converters/ConverterService.cs index 07581a86a8..385d80c1fe 100644 --- a/src/ReactiveUI/Bindings/Converters/ConverterService.cs +++ b/src/ReactiveUI/Bindings/Converters/ConverterService.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -60,9 +60,9 @@ public sealed class ConverterService /// public ConverterService() { - TypedConverters = new BindingTypeConverterRegistry(); - FallbackConverters = new BindingFallbackConverterRegistry(); - SetMethodConverters = new SetMethodBindingConverterRegistry(); + TypedConverters = new(); + FallbackConverters = new(); + SetMethodConverters = new(); } /// @@ -145,22 +145,16 @@ public ConverterService() /// /// public object? ResolveConverter( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type toType) { ArgumentExceptionHelper.ThrowIfNull(fromType); ArgumentExceptionHelper.ThrowIfNull(toType); - // Phase 1: Try exact type-pair match (typed converters) var typed = TypedConverters.TryGetConverter(fromType, toType); - if (typed is not null) - { - return typed; - } - - // Phase 2: Try fallback converters (runtime type checking) - var fallback = FallbackConverters.TryGetConverter(fromType, toType); - return fallback; + return typed ?? (object?)FallbackConverters.TryGetConverter(fromType, toType); } /// @@ -181,9 +175,9 @@ public ConverterService() /// /// public ISetMethodBindingConverter? ResolveSetMethodConverter( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? fromType, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? toType) - { - return SetMethodConverters.TryGetConverter(fromType, toType); - } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type? fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type? toType) => + SetMethodConverters.TryGetConverter(fromType, toType); } diff --git a/src/ReactiveUI/Bindings/Converters/RxConverters.cs b/src/ReactiveUI/Bindings/Converters/RxConverters.cs index d76908f022..35450a98a6 100644 --- a/src/ReactiveUI/Bindings/Converters/RxConverters.cs +++ b/src/ReactiveUI/Bindings/Converters/RxConverters.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Bindings/Converters/SetMethodBindingConverterRegistry.cs b/src/ReactiveUI/Bindings/Converters/SetMethodBindingConverterRegistry.cs index 1fe222bd19..f0f8e600d9 100644 --- a/src/ReactiveUI/Bindings/Converters/SetMethodBindingConverterRegistry.cs +++ b/src/ReactiveUI/Bindings/Converters/SetMethodBindingConverterRegistry.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -85,13 +85,11 @@ public void Register(ISetMethodBindingConverter converter) lock (_gate) { - var snap = _snapshot ?? new Snapshot(new List(8)); + var snap = _snapshot ?? new Snapshot(new(8)); - // Copy-on-write update: clone the list - var newList = new List(snap.Converters) { converter }; + List newList = [..snap.Converters, converter]; - // Publish the new snapshot (atomic via reference assignment) - _snapshot = new Snapshot(newList); + _snapshot = new(newList); } } @@ -115,8 +113,10 @@ public void Register(ISetMethodBindingConverter converter) /// /// public ISetMethodBindingConverter? TryGetConverter( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? fromType, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? toType) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type? fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type? toType) { var snap = Volatile.Read(ref _snapshot); if (snap is null) @@ -124,7 +124,6 @@ public void Register(ISetMethodBindingConverter converter) return null; } - // Find the converter with the highest affinity ISetMethodBindingConverter? best = null; var bestScore = -1; @@ -162,7 +161,6 @@ public IEnumerable GetAllConverters() return []; } - // Return a copy to avoid exposing internal list return [.. snap.Converters]; } diff --git a/src/ReactiveUI/Bindings/IBindingFallbackConverter.cs b/src/ReactiveUI/Bindings/IBindingFallbackConverter.cs index 1c7bdedb2f..9127dd6599 100644 --- a/src/ReactiveUI/Bindings/IBindingFallbackConverter.cs +++ b/src/ReactiveUI/Bindings/IBindingFallbackConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -58,7 +58,9 @@ public interface IBindingFallbackConverter : IEnableLogger /// Results should be cached internally where appropriate. /// /// - int GetAffinityForObjects([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType); + int GetAffinityForObjects( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType); /// /// Attempts to convert the value to the target type. @@ -81,5 +83,10 @@ public interface IBindingFallbackConverter : IEnableLogger /// null as the parameter. /// /// - bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, object from, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, object? conversionHint, [NotNullWhen(true)] out object? result); + bool TryConvert( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + object from, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, + object? conversionHint, + [NotNullWhen(true)] out object? result); } diff --git a/src/ReactiveUI/Bindings/IBindingTypeConverter.cs b/src/ReactiveUI/Bindings/IBindingTypeConverter.cs index e26296508f..d113e34747 100644 --- a/src/ReactiveUI/Bindings/IBindingTypeConverter.cs +++ b/src/ReactiveUI/Bindings/IBindingTypeConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Bindings/IBindingTypeConverter{TFrom,TTo}.cs b/src/ReactiveUI/Bindings/IBindingTypeConverter{TFrom,TTo}.cs index 8ec2502cc0..03fb3ad8d0 100644 --- a/src/ReactiveUI/Bindings/IBindingTypeConverter{TFrom,TTo}.cs +++ b/src/ReactiveUI/Bindings/IBindingTypeConverter{TFrom,TTo}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -22,7 +22,7 @@ namespace ReactiveUI; /// are known at compile time. /// /// -public interface IBindingTypeConverter : IBindingTypeConverter +public interface IBindingTypeConverter : IBindingTypeConverter { /// /// Convert a value to the target type in a type-safe manner. @@ -42,5 +42,5 @@ public interface IBindingTypeConverter : IBindingTypeConverter /// to nullable targets. /// /// - bool TryConvert(TFrom? from, object? conversionHint, [MaybeNullWhen(true)] out TTo? result); + bool TryConvert(TFrom? from, object? conversionHint, out TTo? result); } diff --git a/src/ReactiveUI/Bindings/ISetMethodBindingConverter.cs b/src/ReactiveUI/Bindings/ISetMethodBindingConverter.cs index 06702cbdfc..96933e14db 100644 --- a/src/ReactiveUI/Bindings/ISetMethodBindingConverter.cs +++ b/src/ReactiveUI/Bindings/ISetMethodBindingConverter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Bindings/Interaction/IInteractionBinderImplementation.cs b/src/ReactiveUI/Bindings/Interaction/IInteractionBinderImplementation.cs index 08354bc57a..b1b104edd5 100644 --- a/src/ReactiveUI/Bindings/Interaction/IInteractionBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Interaction/IInteractionBinderImplementation.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/Bindings/Interaction/InteractionBinderImplementation.cs b/src/ReactiveUI/Bindings/Interaction/InteractionBinderImplementation.cs index 5d67d4609d..3723e74cca 100644 --- a/src/ReactiveUI/Bindings/Interaction/InteractionBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Interaction/InteractionBinderImplementation.cs @@ -1,8 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reactive.Disposables; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -16,28 +21,33 @@ public IDisposable BindInteraction( TViewModel? viewModel, TView view, Expression>> propertyName, - Func, Task> handler) // TODO: Create Test - where TViewModel : class - where TView : class, IViewFor + Func, Task> handler) + where TViewModel : class + where TView : class, IViewFor { ArgumentExceptionHelper.ThrowIfNull(propertyName); ArgumentExceptionHelper.ThrowIfNull(handler); var vmExpression = Reflection.Rewrite(propertyName.Body); - var vmNulls = view.WhenAnyValue(x => x.ViewModel).Where(x => x is null).Select(_ => default(IInteraction)); - var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression) - .Cast?>() - .Merge(vmNulls); - - var interactionDisposable = new SerialDisposable(); + var vmNulls = new ChooseObservable?>( + view.WhenAnyValue(x => x.ViewModel), + static x => x is null ? (true, (IInteraction?)null) : (false, null)); + var source = new MergeObservable?>( + [ + new SelectObservable?>( + Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression), + static x => (IInteraction?)x), + vmNulls, + ]); - return source - .Do(x => interactionDisposable.Disposable = x is null - ? System.Reactive.Disposables.Disposable.Empty - : x.RegisterHandler(handler)) - .Finally(() => interactionDisposable.Dispose()) - .Subscribe(_ => { }, ex => this.Log().Error(ex, $"{vmExpression} Interaction Binding received an Exception!")); + var registration = new SwapDisposable(); + var subscription = source.Subscribe(new InteractionRegistrationObserver( + registration, + x => x is null ? EmptyDisposable.Instance : x.RegisterHandler(handler), + this, + $"{vmExpression} Interaction Binding received an Exception!")); + return new CompositeDisposable(subscription, registration); } /// @@ -46,27 +56,279 @@ public IDisposable BindInteraction>> propertyName, - Func, IObservable> handler) // TODO: Create Test - where TViewModel : class - where TView : class, IViewFor + Func, IObservable> handler) + where TViewModel : class + where TView : class, IViewFor { ArgumentExceptionHelper.ThrowIfNull(propertyName); ArgumentExceptionHelper.ThrowIfNull(handler); var vmExpression = Reflection.Rewrite(propertyName.Body); - var vmNulls = view.WhenAnyValue(x => x.ViewModel).Where(x => x is null).Select(_ => default(IInteraction)); - var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression) - .Cast?>() - .Merge(vmNulls); + var vmNulls = new ChooseObservable?>( + view.WhenAnyValue(x => x.ViewModel), + static x => x is null ? (true, (IInteraction?)null) : (false, null)); + var source = new MergeObservable?>( + [ + new SelectObservable?>( + Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression), + static x => (IInteraction?)x), + vmNulls, + ]); + + var registration = new SwapDisposable(); + var subscription = source.Subscribe(new InteractionRegistrationObserver( + registration, + x => x is null ? EmptyDisposable.Instance : x.RegisterHandler(handler), + this, + $"{vmExpression} Interaction Binding received an Exception!")); + return new CompositeDisposable(subscription, registration); + } + + /// Projects each value of a source through a selector. Specialised interaction-binding projection. + /// The source element type. + /// The projected element type. + /// The source observable. + /// Projects a source value into a result. + private sealed class SelectObservable(IObservable source, Func selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } + + /// Applies the selector to each value and forwards the result. + /// The observer receiving projected values. + /// Projects a source value into a result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver + { + /// + public void OnNext(TIn value) + { + TOut result; + try + { + result = selector(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// Forwards only the values chosen by a chooser. Specialised interaction-binding filter-map. + /// The source element type. + /// The forwarded element type. + /// The source observable. + /// Maps a source value to (forward, value); when forward is false the value is skipped. + private sealed class ChooseObservable(IObservable source, Func chooser) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, chooser)); + } + + /// Applies the chooser to each value and forwards only the chosen ones. + /// The observer receiving chosen values. + /// Maps a source value to (forward, value). + private sealed class Sink(IObserver downstream, Func chooser) : IObserver + { + /// + public void OnNext(TIn value) + { + (bool HasValue, TOut Value) result; + try + { + result = chooser(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + if (!result.HasValue) + { + return; + } + + downstream.OnNext(result.Value); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// Forwards the values of every source and completes once all complete. Specialised interaction-binding merge. + /// The element type. + /// The sources to merge. + private sealed class MergeObservable(IObservable[] sources) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, sources); + } + + /// Forwards every source value under a gate and completes once all sources complete. + private sealed class Sink : IDisposable + { + /// Guards downstream delivery and the completion counter. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving the merged values. + private readonly IObserver _downstream; + + /// The subscriptions to each source. + private readonly IDisposable?[] _subscriptions; + + /// The number of sources. + private readonly int _count; + + /// The number of sources that have completed. + private int _doneCount; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Initializes a new instance of the class and subscribes to every source. + /// The observer receiving the merged values. + /// The sources to merge. + public Sink(IObserver downstream, IObservable[] sources) + { + _downstream = downstream; + _count = sources.Length; + _subscriptions = new IDisposable?[sources.Length]; + for (var i = 0; i < sources.Length; i++) + { + _subscriptions[i] = sources[i].Subscribe(new Element(this)); + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Forwards one source value to the downstream. + /// The value to forward. + private void OnNextAt(T value) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _downstream.OnNext(value); + } + } + + /// Forwards an error from any source. + /// The error to forward. + private void OnErrorAt(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _downstream.OnError(error); + } + + /// Completes the downstream once every source has completed. + private void OnCompletedAt() + { + lock (_gate) + { + if (_stopped || ++_doneCount < _count) + { + return; + } + + _stopped = true; + } + + _downstream.OnCompleted(); + } + + /// Routes one source's notifications to the parent sink. + /// The owning sink. + private sealed class Element(Sink parent) : IObserver + { + /// + public void OnNext(T value) => parent.OnNextAt(value); + + /// + public void OnError(Exception error) => parent.OnErrorAt(error); + + /// + public void OnCompleted() => parent.OnCompletedAt(); + } + } + } + + /// + /// Registers the latest interaction's handler (swapping out the previous registration), logs binding errors, and + /// disposes the registration when the source terminates. Fuses the prior Do + Finally + Subscribe. + /// + /// The interaction input type. + /// The interaction output type. + /// Holds the current handler registration, disposing the previous on assignment. + /// Registers a handler for the supplied interaction (or a no-op for null). + /// The object used for logging. + /// Logged when the binding errors. + private sealed class InteractionRegistrationObserver( + SwapDisposable registration, + Func?, IDisposable> register, + IEnableLogger logHost, + string errorMessage) : IObserver?> + { + /// + public void OnNext(IInteraction? value) => registration.Disposable = register(value); - var interactionDisposable = new SerialDisposable(); + /// + public void OnError(Exception error) + { + logHost.Log().Error(error, errorMessage); + registration.Dispose(); + } - return source - .Do(x => interactionDisposable.Disposable = x is null - ? System.Reactive.Disposables.Disposable.Empty - : x.RegisterHandler(handler)) - .Finally(() => interactionDisposable.Dispose()) - .Subscribe(_ => { }, ex => this.Log().Error(ex, $"{vmExpression} Interaction Binding received an Exception!")); + /// + public void OnCompleted() => registration.Dispose(); } } diff --git a/src/ReactiveUI/Bindings/Interaction/InteractionBindingMixins.cs b/src/ReactiveUI/Bindings/Interaction/InteractionBindingMixins.cs index f01f6d7293..8a8f5e5543 100644 --- a/src/ReactiveUI/Bindings/Interaction/InteractionBindingMixins.cs +++ b/src/ReactiveUI/Bindings/Interaction/InteractionBindingMixins.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// @@ -28,6 +30,9 @@ namespace ReactiveUI; /// public static class InteractionBindingMixins { + /// + /// The binder implementation that handles interaction registrations. + /// private static readonly InteractionBinderImplementation _binderImplementation = new(); /// @@ -55,13 +60,13 @@ public static IDisposable BindInteraction( TViewModel? viewModel, Expression>> propertyName, Func, Task> handler) - where TViewModel : class - where TView : class, IViewFor => + where TViewModel : class + where TView : class, IViewFor => _binderImplementation.BindInteraction( - viewModel, - view, - propertyName, - handler); + viewModel, + view, + propertyName, + handler); /// /// Binds an interaction from a view model to a view, allowing the view to handle interaction requests using the @@ -89,8 +94,8 @@ public static IDisposable BindInteraction>> propertyName, Func, IObservable> handler) - where TViewModel : class - where TView : class, IViewFor => + where TViewModel : class + where TView : class, IViewFor => _binderImplementation.BindInteraction( viewModel, view, diff --git a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs index 436a93c377..2ba4634c66 100644 --- a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// @@ -12,146 +14,270 @@ namespace ReactiveUI; public interface IPropertyBinderImplementation : IEnableLogger { /// - /// Creates a two-way binding between a view model and a view. - /// This binding will attempt to convert the values of the - /// view and view model properties using a - /// if they are not of the same type. + /// Creates a two-way binding between a view model and a view using a conversion hint and no converter overrides. + /// + /// The type of the view model that is bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type used only to signal view updates. + /// The instance of the view model object to be bound. + /// The instance of the view object to be bound. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An observable that signals when the view property has changed. + /// An object that can provide a hint for the converter. + /// An instance of IDisposable that, when disposed, disconnects the binding. + IReactiveBinding Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint) + where TViewModel : class + where TView : class, IViewFor; + + /// + /// Creates a two-way binding between a view model and a view using a conversion hint and an optional vm-to-view converter override. + /// + /// The type of the view model that is bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type used only to signal view updates. + /// The instance of the view model object to be bound. + /// The instance of the view object to be bound. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An observable that signals when the view property has changed. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from view model to view property. + /// An instance of IDisposable that, when disposed, disconnects the binding. + IReactiveBinding Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) + where TViewModel : class + where TView : class, IViewFor; + + /// + /// Creates a two-way binding between a view model and a view using a conversion hint and converter overrides. + /// + /// The type of the view model that is bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type used only to signal view updates. + /// The instance of the view model object to be bound. + /// The instance of the view object to be bound. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An observable that signals when the view property has changed. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from view model to view property. + /// An optional converter to use when converting from view to view model property. + /// An instance of IDisposable that, when disposed, disconnects the binding. + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload is part of the public binding API surface; the parameter count is intentional.")] + IReactiveBinding Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride, + IBindingTypeConverter? viewToViewModelConverterOverride) + where TViewModel : class + where TView : class, IViewFor; + + /// + /// Creates a two-way binding between a view model and a view with full control over converter overrides and trigger direction. /// /// The type of the view model that is bound. /// The type of the view being bound. - /// The type of the property bound on the view model. - /// The type of the property bound on the view. - /// A dummy type, only the fact that - /// emits values is considered, not the actual values emitted. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type used only to signal view updates. /// The instance of the view model object to be bound. /// The instance of the view object to be bound. - /// An expression representing the property to be bound to on the view model. - /// This can be a child property, for example x => x.Foo.Bar.Baz in which case - /// the binding will attempt to subscribe recursively to updates in order to - /// always get and set the correct property. - /// An expression representing the property to be bound to on the view. - /// This can be a child property, for example x => x.Foo.Bar.Baz in which case - /// the binding will attempt to subscribe recursively to updates in order to - /// always get and set the correct property. - /// An observable, that when signaled, indicates that the view property - /// has been changed, and that the binding should update the view model - /// property accordingly. - /// An object that can provide a hint for the converter. - /// The semantics of this object is defined by the converter used. - /// An optional to use when converting from the - /// viewModel to view property. - /// An optional to use when converting from the - /// view to viewModel property. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An observable that signals when the view property has changed. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from view model to view property. + /// An optional converter to use when converting from view to view model property. /// The trigger update direction. - /// - /// An instance of that, when disposed, - /// disconnects the binding. - /// - IReactiveBinding Bind( + /// An instance of IDisposable that, when disposed, disconnects the binding. + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload is part of the public binding API surface; the parameter count is intentional.")] + IReactiveBinding Bind( TViewModel? viewModel, TView view, - Expression> vmProperty, - Expression> viewProperty, + Expression> viewModelProperty, + Expression> viewProperty, IObservable? signalViewUpdate, object? conversionHint, - IBindingTypeConverter? vmToViewConverterOverride = null, - IBindingTypeConverter? viewToVMConverterOverride = null, - TriggerUpdate triggerUpdate = TriggerUpdate.ViewToViewModel) + IBindingTypeConverter? viewModelToViewConverterOverride, + IBindingTypeConverter? viewToViewModelConverterOverride, + TriggerUpdate triggerUpdate) where TViewModel : class where TView : class, IViewFor; /// - /// Creates a two-way binding between a view model and a view. - /// This binding will attempt to convert the values of the - /// view and view model properties using a - /// if they are not of the same type. + /// Creates a two-way binding between a view model and a view using explicit converter delegates with default trigger direction. /// /// The type of the view model that is bound. /// The type of the view being bound. - /// The type of the property bound on the view model. - /// The type of the property bound on the view. - /// A dummy type, only the fact that - /// emits values is considered, not the actual values emitted. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type used only to signal view updates. /// The instance of the view model object to be bound. /// The instance of the view object to be bound. - /// An expression representing the property to be bound to on the view model. - /// This can be a child property, for example x => x.Foo.Bar.Baz in which case - /// the binding will attempt to subscribe recursively to updates in order to - /// always get and set the correct property. - /// An expression representing the property to be bound to on the view. - /// This can be a child property, for example x => x.Foo.Bar.Baz in which case - /// the binding will attempt to subscribe recursively to updates in order to - /// always get and set the correct property. - /// An observable, that when signaled, indicates that the view property - /// has been changed, and that the binding should update the view model - /// property accordingly. - /// Delegate to convert the value of the view model's property's type to a value of the - /// view's property's type. - /// Delegate to convert the value of the view's property's type to a value of the - /// view model's property's type. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An observable that signals when the view property has changed. + /// Delegate to convert the view model property value to the view property type. + /// Delegate to convert the view property value to the view model property type. + /// An instance of IDisposable that, when disposed, disconnects the binding. + IReactiveBinding Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + Func viewModelToViewConverter, + Func viewToViewModelConverter) + where TViewModel : class + where TView : class, IViewFor; + + /// + /// Creates a two-way binding between a view model and a view using explicit converter delegates and a specified trigger direction. + /// + /// The type of the view model that is bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type used only to signal view updates. + /// The instance of the view model object to be bound. + /// The instance of the view object to be bound. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An observable that signals when the view property has changed. + /// Delegate to convert the view model property value to the view property type. + /// Delegate to convert the view property value to the view model property type. /// The trigger update direction. - /// - /// An instance of that, when disposed, - /// disconnects the binding. - /// - IReactiveBinding Bind( + /// An instance of IDisposable that, when disposed, disconnects the binding. + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload is part of the public binding API surface; the parameter count is intentional.")] + IReactiveBinding Bind( TViewModel? viewModel, TView view, - Expression> vmProperty, - Expression> viewProperty, + Expression> viewModelProperty, + Expression> viewProperty, IObservable? signalViewUpdate, - Func vmToViewConverter, - Func viewToVmConverter, - TriggerUpdate triggerUpdate = TriggerUpdate.ViewToViewModel) + Func viewModelToViewConverter, + Func viewToViewModelConverter, + TriggerUpdate triggerUpdate) where TViewModel : class where TView : class, IViewFor; /// - /// Creates a one-way binding, i.e. a binding that flows from the - /// to the only. This binding will - /// attempt to convert the value of the view model property to the view property if they - /// are not of the same type. + /// Creates a one-way binding from view model to view using default converters and conversion hint. /// /// The type of the view model that is bound. /// The type of the view that is bound. - /// The type of the property bound on the view model. - /// The type of the property bound on the view. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. /// The instance of the view model to bind to. /// The instance of the view to bind to. - /// - /// An expression representing the property to be bound to on the view model. - /// This can be a child property, for example x => x.Foo.Bar.Baz in which case - /// the binding will attempt to subscribe recursively to updates in order to - /// always get the last value of the property chain. - /// - /// - /// An expression representing the property to be bound to on the view. - /// This can be a child property, for example x => x.Foo.Bar.Baz in which case - /// the binding will attempt to subscribe recursively to updates in order to - /// always set the correct property. - /// - /// - /// An object that can provide a hint for the converter. - /// The semantics of this object is defined by the converter used. - /// - /// - /// An optional to use when converting from the - /// viewModel to view property. - /// - /// - /// An instance of that, when disposed, - /// disconnects the binding. - /// - /// - /// There is no registered converter from to . - /// - IReactiveBinding OneWayBind( + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An instance of IDisposable that, when disposed, disconnects the binding. + IReactiveBinding OneWayBind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty) + where TViewModel : class + where TView : class, IViewFor; + + /// + /// Creates a one-way binding from view model to view with an optional converter override and default conversion hint. + /// + /// The type of the view model that is bound. + /// The type of the view that is bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// The instance of the view model to bind to. + /// The instance of the view to bind to. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An optional converter to use when converting from view model to view property. + /// An instance of IDisposable that, when disposed, disconnects the binding. + IReactiveBinding OneWayBind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IBindingTypeConverter? vmToViewConverterOverride) + where TViewModel : class + where TView : class, IViewFor; + + /// + /// Creates a one-way binding from view model to view using only a conversion hint. + /// + /// The type of the view model that is bound. + /// The type of the view that is bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// The instance of the view model to bind to. + /// The instance of the view to bind to. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An object that can provide a hint for the converter. + /// An instance of IDisposable that, when disposed, disconnects the binding. + IReactiveBinding OneWayBind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint) + where TViewModel : class + where TView : class, IViewFor; + + /// + /// Creates a one-way binding from view model to view with an optional converter override. + /// + /// The type of the view model that is bound. + /// The type of the view that is bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// The instance of the view model to bind to. + /// The instance of the view to bind to. + /// An expression representing the property to be bound on the view model. + /// An expression representing the property to be bound on the view. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from view model to view property. + /// An instance of IDisposable that, when disposed, disconnects the binding. + IReactiveBinding OneWayBind( TViewModel? viewModel, TView view, - Expression> vmProperty, - Expression> viewProperty, + Expression> viewModelProperty, + Expression> viewProperty, object? conversionHint, - IBindingTypeConverter? vmToViewConverterOverride = null) + IBindingTypeConverter? viewModelToViewConverterOverride) where TViewModel : class where TView : class, IViewFor; @@ -166,7 +292,7 @@ IReactiveBinding OneWayBind( /// The return type of the . /// The instance of the view model to bind to. /// The instance of the view to bind to. - /// + /// /// An expression representing the property to be bound to on the view model. /// This can be a child property, for example x => x.Foo.Bar.Baz in which case /// the binding will attempt to subscribe recursively to updates in order to @@ -189,41 +315,63 @@ IReactiveBinding OneWayBind( IReactiveBinding OneWayBind( TViewModel? viewModel, TView view, - Expression> vmProperty, + Expression> viewModelProperty, Expression> viewProperty, Func selector) where TViewModel : class where TView : class, IViewFor; /// - /// BindTo takes an Observable stream and applies it to a target - /// property. Conceptually it is similar to. Subscribe(x => - /// target.property = x), but allows you to use child properties - /// without the null checks. + /// Binds an observable stream to a target property using default converters and conversion hint. /// /// The value type. /// The target type. - /// The target value type. + /// The target value type. /// The target observable to bind to. /// The target object whose property will be set. - /// - /// An expression representing the target property to set. - /// This can be a child property (i.e. x.Foo.Bar.Baz). - /// - /// - /// An object that can provide a hint for the converter. - /// The semantics of this object is defined by the converter used. - /// - /// - /// An optional to use when converting from the - /// viewModel to view property. - /// + /// An expression representing the target property to set. + /// An object that when disposed, disconnects the binding. + IDisposable BindTo( + IObservable observedChange, + TTarget? target, + Expression> propertyExpression) + where TTarget : class; + + /// + /// Binds an observable stream to a target property using only a conversion hint. + /// + /// The value type. + /// The target type. + /// The target value type. + /// The target observable to bind to. + /// The target object whose property will be set. + /// An expression representing the target property to set. + /// An object that can provide a hint for the converter. + /// An object that when disposed, disconnects the binding. + IDisposable BindTo( + IObservable observedChange, + TTarget? target, + Expression> propertyExpression, + object? conversionHint) + where TTarget : class; + + /// + /// Binds an observable stream to a target property with an optional converter override. + /// + /// The value type. + /// The target type. + /// The target value type. + /// The target observable to bind to. + /// The target object whose property will be set. + /// An expression representing the target property to set. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from the observable value to the target property type. /// An object that when disposed, disconnects the binding. - IDisposable BindTo( + IDisposable BindTo( IObservable observedChange, TTarget? target, - Expression> propertyExpression, + Expression> propertyExpression, object? conversionHint, - IBindingTypeConverter? vmToViewConverterOverride = null) + IBindingTypeConverter? viewModelToViewConverterOverride) where TTarget : class; } diff --git a/src/ReactiveUI/Bindings/Property/Internal/BindingConverterResolver.cs b/src/ReactiveUI/Bindings/Property/Internal/BindingConverterResolver.cs index 4fd26a87d1..97dd590adc 100644 --- a/src/ReactiveUI/Bindings/Property/Internal/BindingConverterResolver.cs +++ b/src/ReactiveUI/Bindings/Property/Internal/BindingConverterResolver.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -18,7 +18,10 @@ namespace ReactiveUI; [RequiresUnreferencedCode("Uses RxConverters and Splat which may require dynamic type resolution")] internal class BindingConverterResolver : IBindingConverterResolver { - private static readonly ConcurrentDictionary<(Type fromType, Type? toType), Func?> _setMethodCache = new(); + /// Cache of resolved set-method converter delegates, keyed by (fromType, toType) pair. + private static readonly + ConcurrentDictionary<(Type fromType, Type? toType), Func?> + _setMethodCache = new(); /// public object? GetBindingConverter(Type fromType, Type toType) => @@ -42,9 +45,8 @@ internal class BindingConverterResolver : IBindingConverterResolver return null; } - // Adapt the converter's contract to the local call shape expected by SetThenGet. - // Cache the delegate to ensure reference equality for repeated calls. - return (currentValue, newValue, indexParameters) => converter.PerformSet(currentValue, newValue, indexParameters); + return (currentValue, newValue, indexParameters) => + converter.PerformSet(currentValue, newValue, indexParameters); }); } @@ -70,99 +72,72 @@ internal class BindingConverterResolver : IBindingConverterResolver /// /// /// - private static object? ResolveBestConverter(Type fromType, Type toType) + private static object? ResolveBestConverter(Type fromType, Type toType) => + TryResolveFromConverterService(fromType, toType) + ?? (object?)SelectByHighestAffinity( + AppLocator.Current.GetServices(), + candidate => candidate.FromType == fromType && candidate.ToType == toType ? candidate.GetAffinityForObjects() : 0) + ?? SelectByHighestAffinity( + AppLocator.Current.GetServices(), + candidate => candidate.GetAffinityForObjects(fromType, toType)); + + /// + /// Resolves a converter via , returning null if it is unavailable or throws. + /// + /// The source type. + /// The target type. + /// The resolved converter, or . + private static object? TryResolveFromConverterService(Type fromType, Type toType) { - // Try to use the new ConverterService first (lock-free, optimized) try { - var converter = RxConverters.Current.ResolveConverter(fromType, toType); - if (converter is not null) - { - return converter; - } + return RxConverters.Current.ResolveConverter(fromType, toType); } catch { - // ConverterService not available, fall back to Splat - } - - // Fallback to Splat-based resolution for backward compatibility - // Phase 1: exact-pair typed converters by affinity. - var typed = AppLocator.Current.GetServices(); - var bestTypedScore = -1; - IBindingTypeConverter? bestTyped = null; - - foreach (var c in typed) - { - if (c is null || c.FromType != fromType || c.ToType != toType) - { - continue; - } - - var score = c.GetAffinityForObjects(); - if (score > bestTypedScore && score > 0) - { - bestTypedScore = score; - bestTyped = c; - } - } - - if (bestTyped is not null) - { - return bestTyped; - } - - // Phase 2: fallback converters by affinity. - var fallbacks = AppLocator.Current.GetServices(); - var bestFallbackScore = -1; - IBindingFallbackConverter? bestFallback = null; - - foreach (var c in fallbacks) - { - if (c is null) - { - continue; - } - - var score = c.GetAffinityForObjects(fromType, toType); - if (score > bestFallbackScore && score > 0) - { - bestFallbackScore = score; - bestFallback = c; - } + return null; } - - return bestFallback; } /// - /// Resolves the best for a given pair. + /// Selects the candidate with the highest positive affinity score. /// - /// The inbound runtime type. - /// The target type. - /// The selected converter, or if none matches. - private static ISetMethodBindingConverter? ResolveBestSetMethodConverter(Type fromType, Type? toType) + /// The candidate type. + /// The candidates to evaluate. + /// Returns the affinity score for a candidate. + /// The highest-scoring candidate, or when none has a positive score. + private static T? SelectByHighestAffinity(IEnumerable candidates, Func scoreSelector) + where T : class { - var converters = AppLocator.Current.GetServices(); - var bestScore = -1; - ISetMethodBindingConverter? best = null; + T? best = null; - foreach (var c in converters) + foreach (var candidate in candidates) { - if (c is null) + if (candidate is null) { continue; } - var score = c.GetAffinityForObjects(fromType, toType); + var score = scoreSelector(candidate); if (score > bestScore && score > 0) { bestScore = score; - best = c; + best = candidate; } } return best; } + + /// + /// Resolves the best for a given pair. + /// + /// The inbound runtime type. + /// The target type. + /// The selected converter, or if none matches. + private static ISetMethodBindingConverter? ResolveBestSetMethodConverter(Type fromType, Type? toType) => + SelectByHighestAffinity( + AppLocator.Current.GetServices(), + candidate => candidate.GetAffinityForObjects(fromType, toType)); } diff --git a/src/ReactiveUI/Bindings/Property/Internal/BindingHookEvaluator.cs b/src/ReactiveUI/Bindings/Property/Internal/BindingHookEvaluator.cs index 35dcf48d6e..fe7c0af878 100644 --- a/src/ReactiveUI/Bindings/Property/Internal/BindingHookEvaluator.cs +++ b/src/ReactiveUI/Bindings/Property/Internal/BindingHookEvaluator.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// @@ -28,14 +30,13 @@ public bool EvaluateBindingHooks( var hooks = AppLocator.Current.GetServices(); ArgumentExceptionHelper.ThrowIfNull(view); - // Compile chains once for hook evaluation. var vmChainGetter = vmExpression != null ? new Reflection.CompiledPropertyChain([.. vmExpression.GetExpressionChain()]) : null; - var viewChainGetter = new Reflection.CompiledPropertyChain([.. viewExpression.GetExpressionChain()]); + Reflection.CompiledPropertyChain viewChainGetter = new([.. viewExpression.GetExpressionChain()]); Func[]> vmFetcher = vmExpression is not null - ? (() => + ? () => { if (viewModel is null) { @@ -44,16 +45,15 @@ public bool EvaluateBindingHooks( vmChainGetter!.TryGetAllValues(viewModel, out var fetchedValues); return fetchedValues; - }) - : (() => [new ObservedChange(null!, null, viewModel)]); + } + : () => [new ObservedChange(null!, null, viewModel)]; - Func[]> vFetcher = () => + var vFetcher = () => { viewChainGetter.TryGetAllValues(view, out var fetchedValues); return fetchedValues; }; - // Replace Aggregate with a loop to avoid enumerator overhead and closures. var shouldBind = true; foreach (var hook in hooks) { @@ -62,7 +62,7 @@ public bool EvaluateBindingHooks( continue; } - if (!hook.ExecuteHook(viewModel, view!, vmFetcher!, vFetcher!, direction)) + if (!hook.ExecuteHook(viewModel, view, vmFetcher!, vFetcher!, direction)) { shouldBind = false; break; diff --git a/src/ReactiveUI/Bindings/Property/Internal/IBindingConverterResolver.cs b/src/ReactiveUI/Bindings/Property/Internal/IBindingConverterResolver.cs index 85318fb98b..1617636565 100644 --- a/src/ReactiveUI/Bindings/Property/Internal/IBindingConverterResolver.cs +++ b/src/ReactiveUI/Bindings/Property/Internal/IBindingConverterResolver.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Bindings/Property/Internal/IBindingHookEvaluator.cs b/src/ReactiveUI/Bindings/Property/Internal/IBindingHookEvaluator.cs index a3ea330195..099b4b7834 100644 --- a/src/ReactiveUI/Bindings/Property/Internal/IBindingHookEvaluator.cs +++ b/src/ReactiveUI/Bindings/Property/Internal/IBindingHookEvaluator.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/Bindings/Property/Internal/IPropertyBindingExpressionCompiler.cs b/src/ReactiveUI/Bindings/Property/Internal/IPropertyBindingExpressionCompiler.cs index 9f885fb55b..1ee33484a6 100644 --- a/src/ReactiveUI/Bindings/Property/Internal/IPropertyBindingExpressionCompiler.cs +++ b/src/ReactiveUI/Bindings/Property/Internal/IPropertyBindingExpressionCompiler.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// @@ -87,6 +89,10 @@ internal interface IPropertyBindingExpressionCompiler /// Direct set observables are optimized for simple property bindings without intermediate /// object navigation. They don't need to track host changes. /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IObservable<(bool ShouldEmit, TValue Value)> CreateDirectSetObservable( TTarget? target, IObservable observedChanged, @@ -114,6 +120,10 @@ internal interface IPropertyBindingExpressionCompiler /// Chained set observables handle complex property paths where intermediate objects can change. /// They track host changes and replay values when the host (e.g., ViewModel) changes. /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IObservable<(bool ShouldEmit, TValue Value)> CreateChainedSetObservable( TTarget? target, IObservable observedChanged, diff --git a/src/ReactiveUI/Bindings/Property/Internal/PropertyBindingExpressionCompiler.cs b/src/ReactiveUI/Bindings/Property/Internal/PropertyBindingExpressionCompiler.cs index 2e3ea3d643..509f9a0fc2 100644 --- a/src/ReactiveUI/Bindings/Property/Internal/PropertyBindingExpressionCompiler.cs +++ b/src/ReactiveUI/Bindings/Property/Internal/PropertyBindingExpressionCompiler.cs @@ -1,10 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reactive.Disposables; using System.Reflection; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -15,8 +19,10 @@ namespace ReactiveUI; /// and observable creation for property bindings. It abstracts the complexity of expression tree /// manipulation and compilation required for reactive property bindings. /// -[RequiresUnreferencedCode("Uses reflection over expression trees and compiled property accessors which may be trimmed.")] -[RequiresDynamicCode("Uses dynamic binding paths which may require runtime code generation or reflection-based invocation.")] +[RequiresUnreferencedCode( + "Uses reflection over expression trees and compiled property accessors which may be trimmed.")] +[RequiresDynamicCode( + "Uses dynamic binding paths which may require runtime code generation or reflection-based invocation.")] internal class PropertyBindingExpressionCompiler : IPropertyBindingExpressionCompiler { /// @@ -88,6 +94,10 @@ public bool ShouldReplayOnHostChanges(Expression[]? hostExpressionChain) } /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IObservable<(bool ShouldEmit, TValue Value)> CreateDirectSetObservable( TTarget? target, IObservable observedChanged, @@ -104,16 +114,30 @@ public bool ShouldReplayOnHostChanges(Expression[]? hostExpressionChain) ArgumentExceptionHelper.ThrowIfNull(setter); ArgumentExceptionHelper.ThrowIfNull(getSetConverter); - var synchronizedChanges = observedChanged.Synchronize(); + var synchronizedChanges = new SynchronizeObservable(observedChanged); var setThenGet = CreateSetThenGet(viewExpression, getter, setter, getSetConverter); var arguments = viewExpression.GetArgumentsArray(); - return synchronizedChanges.Select(value => setThenGet(target, value, arguments)) - .Where(result => result.ShouldEmit) - .Select(result => (result.ShouldEmit, result.Value is null ? default! : (TValue)result.Value)); + return new ChooseObservable( + synchronizedChanges, + value => + { + var (shouldEmit, raw) = setThenGet(target, value, arguments); + if (!shouldEmit) + { + return (false, default); + } + + var projected = raw is null ? default! : (TValue)raw; + return (true, (shouldEmit, projected)); + }); } /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IObservable<(bool ShouldEmit, TValue Value)> CreateChainedSetObservable( TTarget? target, IObservable observedChanged, @@ -132,133 +156,338 @@ public bool ShouldReplayOnHostChanges(Expression[]? hostExpressionChain) ArgumentExceptionHelper.ThrowIfNull(setter); ArgumentExceptionHelper.ThrowIfNull(getSetConverter); - var synchronizedChanges = observedChanged.Synchronize(); + var synchronizedChanges = new SynchronizeObservable(observedChanged); var setThenGet = CreateSetThenGet(viewExpression, getter, setter, getSetConverter); var arguments = viewExpression.GetArgumentsArray(); - var hostExpression = viewExpression.GetParent() ?? throw new InvalidOperationException("Host expression was not found."); - var hostChanges = target.WhenAnyDynamic(hostExpression, x => x.Value).Synchronize(); + var hostExpression = viewExpression.GetParent() ?? + throw new InvalidOperationException("Host expression was not found."); + var hostChanges = new SynchronizeObservable(target.WhenAnyDynamic(hostExpression, x => x.Value)); var propertyDefaultValue = CreateDefaultValueForType(viewExpression.Type); var shouldReplayOnHostChanges = ShouldReplayOnHostChanges(hostExpressionChain); - return Observable.Create<(bool ShouldEmit, TValue Value)>(observer => + ChainedSetContext context = new( + setThenGet, + getter, + arguments, + propertyDefaultValue, + shouldReplayOnHostChanges, + hostExpressionChain); + + return new ChainedSetObservable(context, hostChanges, synchronizedChanges, target); + } + + /// + /// Creates the default value instance for used by the "replay on host changes" logic. + /// + /// The member type. + /// + /// A boxed default value for value types, or for reference types. + /// + private static object? CreateDefaultValueForType(Type type) + { + ArgumentExceptionHelper.ThrowIfNull(type); + + return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; + } + + /// + /// Immutable dependencies shared across a single chained set-observable subscription. + /// + /// Sets the value and reads back the effective value, reporting whether it changed. + /// Reads the current value of the bound member. + /// The indexer arguments for the bound member, if any. + /// The default value for the bound member type. + /// Whether the last value should be replayed when the host changes. + /// The expression chain used to resolve the host. + private sealed record ChainedSetContext( + Func SetThenGet, + Func Getter, + object?[]? Arguments, + object? PropertyDefaultValue, + bool ShouldReplayOnHostChanges, + Expression[] HostExpressionChain); + + /// + /// Mutable per-subscription state for a chained set-observable. + /// + private sealed class ChainedSetState + { + /// Gets or sets the most recently resolved host. + public object? LatestHost { get; set; } + + /// Gets or sets the host currently bound to. + public object? CurrentHost { get; set; } + + /// Gets or sets the most recently observed source value. + public object? LastObservedValue { get; set; } + + /// Gets or sets a value indicating whether a source value has been observed. + public bool HasObservedValue { get; set; } + } + + /// Serializes a source's notifications under a per-subscription lock. A tailored Synchronize. + /// The element type. + /// The source observable. + private sealed class SynchronizeObservable(IObservable source) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) { ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, new object())); + } - object? latestHost = null; - object? currentHost = null; - object? lastObservedValue = null; - var hasObservedValue = false; + /// Delivers each notification to the downstream while holding a gate. + /// The observer receiving serialized notifications. + /// The gate held during each notification. + private sealed class Sink(IObserver downstream, object gate) : IObserver + { + /// + public void OnNext(T value) + { + lock (gate) + { + downstream.OnNext(value); + } + } - bool HostPropertyEqualsDefault(object? host) + /// + public void OnError(Exception error) { - if (host is null) + lock (gate) { - return false; + downstream.OnError(error); } + } - var currentValue = getter(host, arguments); - return EqualityComparer.Default.Equals(currentValue, propertyDefaultValue); + /// + public void OnCompleted() + { + lock (gate) + { + downstream.OnCompleted(); + } } + } + } + + /// Forwards only the values chosen by a chooser. Specialised set-observable filter-map. + /// The source element type. + /// The forwarded element type. + /// The source observable. + /// Maps a source value to (forward, value); when forward is false the value is skipped. + private sealed class ChooseObservable(IObservable source, Func chooser) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, chooser)); + } - void ApplyValueToHost(object? host, object? value) + /// Applies the chooser to each value and forwards only the chosen ones. + /// The observer receiving chosen values. + /// Maps a source value to (forward, value). + private sealed class Sink(IObserver downstream, Func chooser) : IObserver + { + /// + public void OnNext(TIn value) { - if (host is null || !hasObservedValue) + (bool HasValue, TOut Value) result; + try { + result = chooser(value); + } + catch (Exception ex) + { + downstream.OnError(ex); return; } - var (shouldEmit, result) = setThenGet(host, value, arguments); - if (!shouldEmit) + if (!result.HasValue) { return; } - observer.OnNext((shouldEmit, result is null ? default! : (TValue)result)); + downstream.OnNext(result.Value); } - var hostDisposable = hostChanges.Subscribe( - hostValue => - { - latestHost = hostValue; + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// + /// On subscription, wires the host and value change streams to the chained-set handlers. A tailored replacement for + /// the prior Observable.Create. + /// + /// The bound member value type. + /// The observed source element type. + /// The chained-set binding context. + /// The host change stream. + /// The synchronized source change stream. + /// The binding target. + [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection, which may be trimmed.")] + [RequiresDynamicCode("Uses dynamic binding paths which may require runtime code generation.")] + private sealed class ChainedSetObservable( + ChainedSetContext context, + IObservable hostChanges, + IObservable synchronizedChanges, + object target) : IObservable<(bool ShouldEmit, TValue Value)> + { + /// + public IDisposable Subscribe(IObserver<(bool ShouldEmit, TValue Value)> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); - if (ReferenceEquals(hostValue, currentHost)) - { - return; - } + ChainedSetState state = new(); - currentHost = hostValue; + var hostDisposable = hostChanges.Subscribe(new DelegateObserver( + hostValue => OnHostChanged(context, state, observer, hostValue), + observer.OnError)); - if (!shouldReplayOnHostChanges || !hasObservedValue || !HostPropertyEqualsDefault(hostValue)) - { - return; - } + var changeDisposable = synchronizedChanges.Subscribe(new DelegateObserver( + value => OnValueChanged(context, state, target, observer, value), + observer.OnError)); - ApplyValueToHost(hostValue, lastObservedValue); - }, - observer.OnError); + return new CompositeDisposable(hostDisposable, changeDisposable); + } - var changeDisposable = synchronizedChanges.Subscribe( - value => - { - hasObservedValue = true; - lastObservedValue = value; + /// + /// Determines whether the current value of the bound member on equals its type default. + /// + /// The binding context. + /// The host object to inspect. + /// when the member value equals the type default; otherwise . + private static bool HostPropertyEqualsDefault(ChainedSetContext context, object? host) + { + if (host is null) + { + return false; + } - var host = latestHost; + var currentValue = context.Getter(host, context.Arguments); + return EqualityComparer.Default.Equals(currentValue, context.PropertyDefaultValue); + } - if (hostExpressionChain is not null) - { - host = ResolveHostFromChain(target, hostExpressionChain); - latestHost = host; - } + /// + /// Applies to and emits the effective value when it changed. + /// + /// The binding context. + /// The mutable subscription state. + /// The observer to emit to. + /// The host object to set the value on. + /// The value to set. + private static void ApplyValueToHost( + ChainedSetContext context, + ChainedSetState state, + IObserver<(bool ShouldEmit, TValue Value)> observer, + object? host, + object? value) + { + if (host is null || !state.HasObservedValue) + { + return; + } - if (host is null) - { - return; - } + var (shouldEmit, result) = context.SetThenGet(host, value, context.Arguments); + if (!shouldEmit) + { + return; + } - ApplyValueToHost(host, value); - }, - observer.OnError); + observer.OnNext((shouldEmit, result is null ? default! : (TValue)result)); + } - return new CompositeDisposable(hostDisposable, changeDisposable); - }); - } + /// + /// Handles a change of the binding host, replaying the last observed value when appropriate. + /// + /// The binding context. + /// The mutable subscription state. + /// The observer to emit to. + /// The new host value. + private static void OnHostChanged( + ChainedSetContext context, + ChainedSetState state, + IObserver<(bool ShouldEmit, TValue Value)> observer, + object? hostValue) + { + state.LatestHost = hostValue; - /// - /// Resolves the current host object for the binding by evaluating the host expression chain. - /// - /// The root binding target. - /// The expression chain used to compute the host. - /// - /// The resolved host object, or if the chain cannot be evaluated. - /// - private static object? ResolveHostFromChain(object target, Expression[] hostExpressionChain) - { - ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentExceptionHelper.ThrowIfNull(hostExpressionChain); + if (ReferenceEquals(hostValue, state.CurrentHost)) + { + return; + } - object? host = target; + state.CurrentHost = hostValue; - if (!Reflection.TryGetValueForPropertyChain(out host, host, hostExpressionChain)) + if (!context.ShouldReplayOnHostChanges || !state.HasObservedValue || !HostPropertyEqualsDefault(context, hostValue)) + { + return; + } + + ApplyValueToHost(context, state, observer, hostValue, state.LastObservedValue); + } + + /// + /// Handles a new observed source value, resolving the current host and applying the value to it. + /// + /// The binding context. + /// The mutable subscription state. + /// The root binding target. + /// The observer to emit to. + /// The newly observed value. + private static void OnValueChanged( + ChainedSetContext context, + ChainedSetState state, + object target, + IObserver<(bool ShouldEmit, TValue Value)> observer, + object? value) { - return null; + state.HasObservedValue = true; + state.LastObservedValue = value; + + var host = state.LatestHost; + + if (context.HostExpressionChain is not null) + { + host = ResolveHostFromChain(target, context.HostExpressionChain); + state.LatestHost = host; + } + + if (host is null) + { + return; + } + + ApplyValueToHost(context, state, observer, host, value); } - return host; - } + /// + /// Resolves the current host object for the binding by evaluating the host expression chain. + /// + /// The root binding target. + /// The expression chain used to compute the host. + /// + /// The resolved host object, or if the chain cannot be evaluated. + /// + private static object? ResolveHostFromChain(object target, Expression[] hostExpressionChain) + { + ArgumentExceptionHelper.ThrowIfNull(target); + ArgumentExceptionHelper.ThrowIfNull(hostExpressionChain); - /// - /// Creates the default value instance for used by the "replay on host changes" logic. - /// - /// The member type. - /// - /// A boxed default value for value types, or for reference types. - /// - private static object? CreateDefaultValueForType(Type type) - { - ArgumentExceptionHelper.ThrowIfNull(type); + var host = target; - return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; + if (!Reflection.TryGetValueForPropertyChain(out host, host, hostExpressionChain)) + { + return null; + } + + return host; + } } } diff --git a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.Conversions.cs b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.Conversions.cs new file mode 100644 index 0000000000..2d0cd9bd5e --- /dev/null +++ b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.Conversions.cs @@ -0,0 +1,332 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; + +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// +/// Conversion and change-projection helpers used by the two-way binding pipeline. +/// +public partial class PropertyBinderImplementation +{ + /// + /// Dispatches a single conversion attempt to a converter, choosing the typed or fallback conversion path. + /// + /// The converter to use. + /// The type being converted from. + /// The type being converted to. + /// The value to convert. + /// An optional hint passed to the converter. + /// The converted value when conversion succeeds. + /// when the converter produced a value; otherwise . + private static bool TryConvertUsingConverter( + object converter, + Type sourceType, + Type targetType, + object? value, + object? conversionHint, + out object? result) + { + switch (converter) + { + case IBindingTypeConverter typedConverter: + return typedConverter.TryConvertTyped(value, conversionHint, out result); + case IBindingFallbackConverter when value is null: + { + result = null; + return false; + } + + case IBindingFallbackConverter fallbackConverter: + return fallbackConverter.TryConvert(sourceType, value, targetType, conversionHint, out result); + default: + { + result = null; + return false; + } + } + } + + /// + /// Attempts a conversion using an explicitly supplied converter, falling back to a registry + /// converter registered for the same type pair when the supplied converter does not apply. + /// + /// The converter explicitly supplied by the caller. + /// The type being converted from. + /// The type being converted to. + /// The value to convert. + /// An optional hint passed to the converter. + /// The converted value when conversion succeeds. + /// when a value was produced; otherwise . + private static bool TryConvertWithOverride( + object converter, + Type sourceType, + Type targetType, + object? value, + object? conversionHint, + out object? converted) + { + if (TryConvertUsingConverter(converter, sourceType, targetType, value, conversionHint, out converted)) + { + return true; + } + + var fallbackConverter = GetConverterForTypes(sourceType, targetType); + if (fallbackConverter is null || fallbackConverter == converter) + { + return false; + } + + return BindingTypeConverterDispatch.TryConvertAny(fallbackConverter, sourceType, value, targetType, conversionHint, out converted); + } + + /// + /// Converts a view model property value into the corresponding view property value. + /// + /// The view model property type. + /// The view property type. + /// The resolved converter, or when no converter applies. + /// The converter explicitly supplied by the caller, if any. + /// An optional hint passed to the converter. + /// The view model value to convert. + /// The converted view value. + /// when a view value was produced; otherwise . + private static bool TryConvertViewModelToView( + object? converter, + IBindingTypeConverter? converterOverride, + object? conversionHint, + TViewModelPropertyType? viewModelValue, + out TViewPropertyType viewValue) + { + if (converter is null) + { + viewValue = viewModelValue is TViewPropertyType typedValue ? typedValue : default!; + return true; + } + + bool success; + object? converted; + + if (converterOverride is not null) + { + success = TryConvertWithOverride(converter, typeof(TViewModelPropertyType), typeof(TViewPropertyType), viewModelValue, conversionHint, out converted); + } + else + { + success = BindingTypeConverterDispatch.TryConvertAny( + converter, + typeof(TViewModelPropertyType), + viewModelValue, + typeof(TViewPropertyType), + conversionHint, + out converted); + + if (!success && typeof(TViewPropertyType).IsAssignableFrom(typeof(TViewModelPropertyType))) + { + viewValue = viewModelValue is TViewPropertyType fallbackValue ? fallbackValue : default!; + return true; + } + } + + viewValue = success ? (TViewPropertyType)converted! : default!; + return success; + } + + /// + /// Converts a view property value back into the corresponding view model property value. + /// + /// The view model property type. + /// The view property type. + /// The resolved converter, or when no converter applies. + /// The converter explicitly supplied by the caller, if any. + /// An optional hint passed to the converter. + /// The view value to convert. + /// The converted view model value. + /// when a view model value was produced; otherwise . + private static bool TryConvertViewToViewModel( + object? converter, + IBindingTypeConverter? converterOverride, + object? conversionHint, + TViewPropertyType viewValue, + out TViewModelPropertyType? viewModelValue) + { + if (converter is null) + { + viewModelValue = viewValue is TViewModelPropertyType typedValue ? typedValue : default; + return true; + } + + bool success; + object? converted; + + if (converterOverride is not null) + { + success = TryConvertWithOverride(converter, typeof(TViewPropertyType), typeof(TViewModelPropertyType?), viewValue, conversionHint, out converted); + } + else + { + success = BindingTypeConverterDispatch.TryConvertAny( + converter, + typeof(TViewPropertyType), + viewValue, + typeof(TViewModelPropertyType?), + conversionHint, + out converted); + + if (!success && typeof(TViewModelPropertyType).IsAssignableFrom(typeof(TViewPropertyType))) + { + viewModelValue = viewValue is TViewModelPropertyType fallbackValue ? fallbackValue : default; + return true; + } + } + + viewModelValue = success ? (TViewModelPropertyType?)converted : default; + return success; + } + + /// + /// Projects a change signal into the resolved value to apply, in the correct direction. + /// + /// The type of the view. + /// The type of the view model property. + /// The type of the view property. + /// when the view model side changed; otherwise the view side changed. + /// The view instance. + /// The compiled getter for the view model property chain. + /// The compiled getter for the view property chain. + /// Converter from view model value to view value. + /// Converter from view value to view model value. + /// Whether a value should be applied, the value to apply, and the direction to apply it. + private static (bool isValid, object? view, bool isViewModel) ProjectChange( + bool isViewModelChange, + TView view, + Reflection.CompiledPropertyChain viewModelChainGetter, + Reflection.CompiledPropertyChain viewChainGetter, + OutFunc viewModelToViewConverter, + OutFunc viewToViewModelConverter) + where TView : class, IViewFor + { + if (!viewModelChainGetter.TryGetValue(view.ViewModel, out var viewModelValue) || + !viewChainGetter.TryGetValue(view, out var viewValue)) + { + return (false, null, false); + } + + if (isViewModelChange) + { + if (!viewModelToViewConverter(viewModelValue, out var viewModelAsView) || + EqualityComparer.Default.Equals(viewValue, viewModelAsView)) + { + return (false, null, false); + } + + return (true, viewModelAsView, true); + } + + if (!viewToViewModelConverter(viewValue, out var viewAsViewModel) || + EqualityComparer.Default.Equals(viewModelValue, viewAsViewModel)) + { + return (false, null, false); + } + + return (true, viewAsViewModel, false); + } + + /// + /// Builds the merged observable that signals when either the view model or the view side changed. + /// + /// The type of the view model. + /// The type of the view. + /// The element type of the optional view update signal. + /// Specifies which direction triggers the initial update. + /// An optional observable that signals view property changes. + /// The view model instance. + /// The view instance. + /// The rewritten view model property expression. + /// An observable that emits when the view property changes. + /// A subject used to push the initial update through the pipeline. + /// An observable emitting for view model changes and for view changes. + private static MergeObservable BuildChangeSource( + TriggerUpdate triggerUpdate, + IObservable? signalViewUpdate, + TViewModel? viewModel, + TView view, + Expression viewModelExpression, + IObservable viewChanges, + BroadcastSubject signalInitialUpdate) + where TViewModel : class + where TView : class, IViewFor + { + var initialUpdateSignal = new SelectObservable(signalInitialUpdate, static _ => true); + var viewModelSignal = new SelectObservable( + Reflection.ViewModelWhenAnyValue(viewModel, view, viewModelExpression), + static _ => true); + + switch (triggerUpdate) + { + case TriggerUpdate.ViewToViewModel: + { + var signalObservable = signalViewUpdate is not null + ? new SelectObservable(signalViewUpdate, static _ => false) + : viewChanges; + + return new MergeObservable([viewModelSignal, initialUpdateSignal, signalObservable]); + } + + default: + { + var primary = signalViewUpdate is null + ? (IObservable)viewModelSignal + : new MergeObservable( + [ + new SelectObservable(signalViewUpdate, static _ => true), + new Take1Observable(viewModelSignal), + ]); + + return new MergeObservable([primary, initialUpdateSignal, viewChanges]); + } + } + } + + /// + /// Bundles the inputs required to create a two-way binding so they can be threaded through the binding pipeline. + /// + /// The type of the view model. + /// The type of the view. + /// The type of the view model property. + /// The type of the view property. + /// The element type of the optional view update signal. + private readonly record struct TwoWayBindRequest + where TViewModel : class + where TView : class, IViewFor + { + /// Gets the view model instance. + public TViewModel? ViewModel { get; init; } + + /// Gets the view instance. + public TView View { get; init; } + + /// Gets the expression for the view model property. + public Expression> ViewModelProperty { get; init; } + + /// Gets the expression for the view property. + public Expression> ViewProperty { get; init; } + + /// Gets an optional observable that signals view property changes. + public IObservable? SignalViewUpdate { get; init; } + + /// Gets the converter from a view model value to a view value. + public OutFunc ViewModelToViewConverter { get; init; } + + /// Gets the converter from a view value to a view model value. + public OutFunc ViewToViewModelConverter { get; init; } + + /// Gets the direction that triggers the initial update. + public TriggerUpdate TriggerUpdate { get; init; } + } +} diff --git a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs index 68b5e63f84..bdcdf33ec6 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs @@ -1,10 +1,15 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reactive.Disposables; +using System.Reactive.Linq; using System.Reflection; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -21,13 +26,20 @@ namespace ReactiveUI; /// /// [RequiresUnreferencedCode("Uses reflection over runtime types and expression graphs which may be trimmed.")] -[RequiresDynamicCode("Uses dynamic binding paths which may require runtime code generation or reflection-based invocation.")] -public class PropertyBinderImplementation : IPropertyBinderImplementation +[RequiresDynamicCode( + "Uses dynamic binding paths which may require runtime code generation or reflection-based invocation.")] +public partial class PropertyBinderImplementation : IPropertyBinderImplementation { - private static readonly IBindingConverterResolver _staticConverterResolver = new BindingConverterResolver(); + /// Shared static converter resolver used for type-based binding lookups. + private static readonly BindingConverterResolver _staticConverterResolver = new(); + /// Resolves binding type converters for this instance. private readonly IBindingConverterResolver _converterResolver; + + /// Compiles property binding expressions to optimized accessors. private readonly IPropertyBindingExpressionCompiler _expressionCompiler; + + /// Evaluates binding hooks before a binding is established. private readonly IBindingHookEvaluator _hookEvaluator; /// @@ -64,7 +76,6 @@ internal PropertyBinderImplementation( /// Initializes static members of the class. /// Ensures ReactiveUI static initialization is performed before bindings are used. /// - /// /// Represents a converter that attempts conversion and returns success via an parameter. /// @@ -76,362 +87,392 @@ internal PropertyBinderImplementation( private delegate bool OutFunc(T1 t1, out T2 t2); /// - public IReactiveBinding Bind( - TViewModel? viewModel, - TView view, - Expression> vmProperty, - Expression> viewProperty, - IObservable? signalViewUpdate, - object? conversionHint, - IBindingTypeConverter? vmToViewConverterOverride = null, - IBindingTypeConverter? viewToVMConverterOverride = null, - TriggerUpdate triggerUpdate = TriggerUpdate.ViewToViewModel) + public IReactiveBinding + Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint) + where TViewModel : class + where TView : class, IViewFor => + Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + signalViewUpdate, + conversionHint, + null, + null, + TriggerUpdate.ViewToViewModel); + + /// + public IReactiveBinding + Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) + where TViewModel : class + where TView : class, IViewFor => + Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + signalViewUpdate, + conversionHint, + viewModelToViewConverterOverride, + null, + TriggerUpdate.ViewToViewModel); + + /// + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload mirrors the public IPropertyBinderImplementation contract; the parameter count is part of the binding API surface.")] + public IReactiveBinding + Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride, + IBindingTypeConverter? viewToViewModelConverterOverride) + where TViewModel : class + where TView : class, IViewFor => + Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + signalViewUpdate, + conversionHint, + viewModelToViewConverterOverride, + viewToViewModelConverterOverride, + TriggerUpdate.ViewToViewModel); + + /// + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload mirrors the public IPropertyBinderImplementation contract; the parameter count is part of the binding API surface.")] + public IReactiveBinding + Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride, + IBindingTypeConverter? viewToViewModelConverterOverride, + TriggerUpdate triggerUpdate) where TViewModel : class where TView : class, IViewFor { - ArgumentExceptionHelper.ThrowIfNull(vmProperty); + ArgumentExceptionHelper.ThrowIfNull(viewModelProperty); ArgumentExceptionHelper.ThrowIfNull(viewProperty); - // First, try to find registered converters (prioritize user-registered converters) - // If an override is provided, use it; otherwise fall back to service locator - var vmToViewConverterObj = vmToViewConverterOverride ?? GetConverterForTypes(typeof(TVMProp), typeof(TVProp)); + var viewModelToViewConverterObj = viewModelToViewConverterOverride ?? GetConverterForTypes(typeof(TViewModelPropertyType), typeof(TViewPropertyType)); - var viewToVMConverterObj = viewToVMConverterOverride ?? GetConverterForTypes(typeof(TVProp), typeof(TVMProp?)); + var viewToViewModelConverterObj = viewToViewModelConverterOverride ?? GetConverterForTypes(typeof(TViewPropertyType), typeof(TViewModelPropertyType?)); - // Check if we have converters or if types are assignable - var hasConverters = vmToViewConverterObj is not null && viewToVMConverterObj is not null; - var typesAreAssignable = typeof(TVProp).IsAssignableFrom(typeof(TVMProp)) || typeof(TVMProp).IsAssignableFrom(typeof(TVProp)); + var hasConverters = viewModelToViewConverterObj is not null && viewToViewModelConverterObj is not null; + var typesAreAssignable = typeof(TViewPropertyType).IsAssignableFrom(typeof(TViewModelPropertyType)) || + typeof(TViewModelPropertyType).IsAssignableFrom(typeof(TViewPropertyType)); if (!hasConverters && !typesAreAssignable) { throw new ArgumentException( - $"Can't two-way convert between {typeof(TVMProp)} and {typeof(TVProp)}. To fix this, register a IBindingTypeConverter or call the version with the converter Func."); + $"Can't two-way convert between {typeof(TViewModelPropertyType)} and {typeof(TViewPropertyType)}. " + + "To fix this, register a IBindingTypeConverter or call the version with the converter Func."); } - bool VmToViewFunc(TVMProp? vmValue, out TVProp vValue) + return BindImpl(new TwoWayBindRequest { - if (vmToViewConverterObj is not null) - { - bool result; - object? tmp; - - // If an explicit override was provided, call it directly (bypassing type checks) - // Otherwise use the dispatch which validates types for auto-discovered converters - if (vmToViewConverterOverride is not null) - { - // Trust the user's explicit converter choice - if (vmToViewConverterObj is IBindingTypeConverter typedConverter) - { - result = typedConverter.TryConvertTyped(vmValue, conversionHint, out tmp); - } - else if (vmToViewConverterObj is IBindingFallbackConverter fallbackConverter) - { - // Fallback converters require non-null input - if (vmValue is null) - { - tmp = null; - result = false; - } - else - { - result = fallbackConverter.TryConvert(typeof(TVMProp), vmValue, typeof(TVProp), conversionHint, out tmp); - } - } - else - { - tmp = null; - result = false; - } - - // If explicit override failed, try to find a better converter from registry - if (!result) - { - var fallbackConverter = GetConverterForTypes(typeof(TVMProp), typeof(TVProp)); - if (fallbackConverter is not null && fallbackConverter != vmToViewConverterObj) - { - result = BindingTypeConverterDispatch.TryConvertAny( - fallbackConverter, - typeof(TVMProp), - vmValue, - typeof(TVProp), - conversionHint, - out tmp); - - if (result) - { - vValue = (TVProp)tmp!; - return true; - } - } - } - } - else - { - // No override - use type-checked dispatch for auto-discovered converter - result = BindingTypeConverterDispatch.TryConvertAny( - vmToViewConverterObj, - typeof(TVMProp), - vmValue, - typeof(TVProp), - conversionHint, - out tmp); - - // If auto-discovered converter failed, try direct assignment - if (!result && typeof(TVProp).IsAssignableFrom(typeof(TVMProp))) - { - vValue = vmValue is TVProp fallbackValue ? fallbackValue : default!; - return true; - } - } + ViewModel = viewModel, + View = view, + ViewModelProperty = viewModelProperty, + ViewProperty = viewProperty, + SignalViewUpdate = signalViewUpdate, + ViewModelToViewConverter = (value, out converted) => + TryConvertViewModelToView(viewModelToViewConverterObj, viewModelToViewConverterOverride, conversionHint, value, out converted), + ViewToViewModelConverter = (value, out converted) => + TryConvertViewToViewModel(viewToViewModelConverterObj, viewToViewModelConverterOverride, conversionHint, value, out converted), + TriggerUpdate = triggerUpdate, + }); + } - vValue = result ? (TVProp)tmp! : default!; - return result; - } + /// + public IReactiveBinding + Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + Func viewModelToViewConverter, + Func viewToViewModelConverter) + where TViewModel : class + where TView : class, IViewFor => + Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + signalViewUpdate, + viewModelToViewConverter, + viewToViewModelConverter, + TriggerUpdate.ViewToViewModel); - // No converter - direct assignment - vValue = vmValue is TVProp typedValue ? typedValue : default!; - return true; - } + /// + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload mirrors the public IPropertyBinderImplementation contract; the parameter count is part of the binding API surface.")] + public IReactiveBinding + Bind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + Func viewModelToViewConverter, + Func viewToViewModelConverter, + TriggerUpdate triggerUpdate) + where TViewModel : class + where TView : class, IViewFor + { + ArgumentExceptionHelper.ThrowIfNull(viewModelProperty); + ArgumentExceptionHelper.ThrowIfNull(viewProperty); + ArgumentExceptionHelper.ThrowIfNull(viewModelToViewConverter); + ArgumentExceptionHelper.ThrowIfNull(viewToViewModelConverter); - bool ViewToVmFunc(TVProp vValue, out TVMProp? vmValue) + return BindImpl(new TwoWayBindRequest { - if (viewToVMConverterObj is not null) + ViewModel = viewModel, + View = view, + ViewModelProperty = viewModelProperty, + ViewProperty = viewProperty, + SignalViewUpdate = signalViewUpdate, + ViewModelToViewConverter = (value, out converted) => { - bool result; - object? tmp; - - // If an explicit override was provided, call it directly (bypassing type checks) - // Otherwise use the dispatch which validates types for auto-discovered converters - if (viewToVMConverterOverride is not null) - { - // Trust the user's explicit converter choice - if (viewToVMConverterObj is IBindingTypeConverter typedConverter) - { - result = typedConverter.TryConvertTyped(vValue, conversionHint, out tmp); - } - else if (viewToVMConverterObj is IBindingFallbackConverter fallbackConverter) - { - // Fallback converters require non-null input - if (vValue is null) - { - tmp = null; - result = false; - } - else - { - result = fallbackConverter.TryConvert(typeof(TVProp), vValue, typeof(TVMProp?), conversionHint, out tmp); - } - } - else - { - tmp = null; - result = false; - } - - // If explicit override failed, try to find a better converter from registry - if (!result) - { - var fallbackConverter = GetConverterForTypes(typeof(TVProp), typeof(TVMProp?)); - if (fallbackConverter is not null && fallbackConverter != viewToVMConverterObj) - { - result = BindingTypeConverterDispatch.TryConvertAny( - fallbackConverter, - typeof(TVProp), - vValue, - typeof(TVMProp?), - conversionHint, - out tmp); - - if (result) - { - vmValue = (TVMProp?)tmp; - return true; - } - } - } - } - else - { - // No override - use type-checked dispatch for auto-discovered converter - result = BindingTypeConverterDispatch.TryConvertAny( - viewToVMConverterObj, - typeof(TVProp), - vValue, - typeof(TVMProp?), - conversionHint, - out tmp); - - // If auto-discovered converter failed, try direct assignment - if (!result && typeof(TVMProp).IsAssignableFrom(typeof(TVProp))) - { - vmValue = vValue is TVMProp fallbackValue ? fallbackValue : default; - return true; - } - } - - vmValue = result ? (TVMProp?)tmp : default; - return result; - } - - // No converter - direct assignment - vmValue = vValue is TVMProp typedValue ? typedValue : default; - return true; - } - - return BindImpl(viewModel, view, vmProperty, viewProperty, signalViewUpdate, VmToViewFunc, ViewToVmFunc, triggerUpdate); + converted = viewModelToViewConverter(value); + return true; + }, + ViewToViewModelConverter = (value, out converted) => + { + converted = viewToViewModelConverter(value); + return true; + }, + TriggerUpdate = triggerUpdate, + }); } /// - public IReactiveBinding Bind( + public IReactiveBinding OneWayBind( TViewModel? viewModel, TView view, - Expression> vmProperty, - Expression> viewProperty, - IObservable? signalViewUpdate, - Func vmToViewConverter, - Func viewToVmConverter, - TriggerUpdate triggerUpdate = TriggerUpdate.ViewToViewModel) + Expression> viewModelProperty, + Expression> viewProperty) where TViewModel : class - where TView : class, IViewFor - { - ArgumentExceptionHelper.ThrowIfNull(vmProperty); - ArgumentExceptionHelper.ThrowIfNull(viewProperty); - ArgumentExceptionHelper.ThrowIfNull(vmToViewConverter); - ArgumentExceptionHelper.ThrowIfNull(viewToVmConverter); - - bool VmToViewFunc(TVMProp? vmValue, out TVProp vValue) - { - vValue = vmToViewConverter(vmValue); - return true; - } + where TView : class, IViewFor => + OneWayBind(viewModel, view, viewModelProperty, viewProperty, null, null); - bool ViewToVmFunc(TVProp vValue, out TVMProp? vmValue) - { - vmValue = viewToVmConverter(vValue); - return true; - } + /// + public IReactiveBinding OneWayBind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + IBindingTypeConverter? vmToViewConverterOverride) + where TViewModel : class + where TView : class, IViewFor => + OneWayBind(viewModel, view, viewModelProperty, viewProperty, null, vmToViewConverterOverride); - return BindImpl(viewModel, view, vmProperty, viewProperty, signalViewUpdate, VmToViewFunc, ViewToVmFunc, triggerUpdate); - } + /// + public IReactiveBinding OneWayBind( + TViewModel? viewModel, + TView view, + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint) + where TViewModel : class + where TView : class, IViewFor => + OneWayBind(viewModel, view, viewModelProperty, viewProperty, conversionHint, null); /// - public IReactiveBinding OneWayBind( + public IReactiveBinding OneWayBind( TViewModel? viewModel, TView view, - Expression> vmProperty, - Expression> viewProperty, - object? conversionHint = null, - IBindingTypeConverter? vmToViewConverterOverride = null) + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) where TViewModel : class where TView : class, IViewFor { - ArgumentExceptionHelper.ThrowIfNull(vmProperty); + ArgumentExceptionHelper.ThrowIfNull(viewModelProperty); ArgumentExceptionHelper.ThrowIfNull(viewProperty); - var vmExpression = Reflection.Rewrite(vmProperty.Body); + var viewModelExpression = Reflection.Rewrite(viewModelProperty.Body); var viewExpression = Reflection.Rewrite(viewProperty.Body); var viewType = viewExpression.Type; - var ret = _hookEvaluator.EvaluateBindingHooks(viewModel, view, vmExpression, viewExpression, BindingDirection.OneWay); + var ret = _hookEvaluator.EvaluateBindingHooks( + viewModel, + view, + viewModelExpression, + viewExpression, + BindingDirection.OneWay); if (!ret) { - return new ReactiveBinding(view, viewExpression, vmExpression, Observable.Empty(), BindingDirection.OneWay, Disposable.Empty); + return new ReactiveBinding( + view, + viewExpression, + viewModelExpression, + Observable.Empty, + BindingDirection.OneWay, + EmptyDisposable.Instance); } - // First, try to find a registered converter (prioritize user-registered converters) - var converterObj = vmToViewConverterOverride ?? GetConverterForTypes(typeof(TVMProp?), viewType); + var converterObj = viewModelToViewConverterOverride ?? GetConverterForTypes(typeof(TViewModelPropertyType?), viewType); if (converterObj is not null) { - // Use the converter - var source = - Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression) - .SelectMany(x => + var source = new ChooseObservable( + Reflection.ViewModelWhenAnyValue(viewModel, view, viewModelExpression), + x => + { + var runtimeType = x?.GetType() ?? typeof(TViewModelPropertyType); + + var convertResult = BindingTypeConverterDispatch.TryConvertAny( + converterObj, + runtimeType, + x, + viewType, + conversionHint, + out var tmp); + + if (convertResult) { - var runtimeType = x?.GetType() ?? typeof(TVMProp); - - // Try converter first - var convertResult = BindingTypeConverterDispatch.TryConvertAny( - converterObj, - runtimeType, - x, - viewType, - conversionHint, - out var tmp); - - if (convertResult) - { - return Observable.Return(tmp); - } - - // Converter failed - // If no override was provided (auto-discovered converter), try direct assignment - if (vmToViewConverterOverride is null) - { - // For null values, use the actual TVMProp type for assignability check - if (viewType.IsAssignableFrom(typeof(TVMProp))) - { - return Observable.Return((object?)x); - } - } - - // Cannot convert - skip this update - return Observable.Empty; - }); - - var (disposable, obs) = BindToDirect(source, view, viewExpression); - return new ReactiveBinding(view, viewExpression, vmExpression, obs, BindingDirection.OneWay, disposable); + return (true, tmp); + } + + return viewModelToViewConverterOverride is null && viewType.IsAssignableFrom(typeof(TViewModelPropertyType)) + ? (true, (object?)x) + : (false, null); + }); + + var (disposable, obs) = BindToDirect(source, view, viewExpression); + return new ReactiveBinding( + view, + viewExpression, + viewModelExpression, + obs, + BindingDirection.OneWay, + disposable); } - // No converter found - check if types are directly assignable - if (viewType.IsAssignableFrom(typeof(TVMProp))) + if (viewType.IsAssignableFrom(typeof(TViewModelPropertyType))) { - // No conversion needed - direct assignment - var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Select(x => (object?)x); - var (disposable, obs) = BindToDirect(source, view, viewExpression); - return new ReactiveBinding(view, viewExpression, vmExpression, obs, BindingDirection.OneWay, disposable); + var source = new SelectObservable( + Reflection.ViewModelWhenAnyValue(viewModel, view, viewModelExpression), + static x => x); + var (disposable, obs) = BindToDirect(source, view, viewExpression); + return new ReactiveBinding( + view, + viewExpression, + viewModelExpression, + obs, + BindingDirection.OneWay, + disposable); } - // No converter and types not assignable - throw exception - throw new ArgumentException($"Can't convert {typeof(TVMProp)} to {viewType}. To fix this, register a IBindingTypeConverter"); + throw new ArgumentException( + $"Can't convert {typeof(TViewModelPropertyType)} to {viewType}. To fix this, register a IBindingTypeConverter"); } /// public IReactiveBinding OneWayBind( TViewModel? viewModel, TView view, - Expression> vmProperty, + Expression> viewModelProperty, Expression> viewProperty, Func selector) where TViewModel : class where TView : class, IViewFor { - ArgumentExceptionHelper.ThrowIfNull(vmProperty); + ArgumentExceptionHelper.ThrowIfNull(viewModelProperty); ArgumentExceptionHelper.ThrowIfNull(viewProperty); - var vmExpression = Reflection.Rewrite(vmProperty.Body); + var viewModelExpression = Reflection.Rewrite(viewModelProperty.Body); var viewExpression = Reflection.Rewrite(viewProperty.Body); - var ret = _hookEvaluator.EvaluateBindingHooks(viewModel, view, vmExpression, viewExpression, BindingDirection.OneWay); + var ret = _hookEvaluator.EvaluateBindingHooks( + viewModel, + view, + viewModelExpression, + viewExpression, + BindingDirection.OneWay); if (!ret) { - return new ReactiveBinding(view, viewExpression, vmExpression, Observable.Empty(), BindingDirection.OneWay, Disposable.Empty); + return new ReactiveBinding( + view, + viewExpression, + viewModelExpression, + Observable.Empty, + BindingDirection.OneWay, + EmptyDisposable.Instance); } - var source = Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Cast().Select(selector); + var source = new SelectObservable( + Reflection.ViewModelWhenAnyValue(viewModel, view, viewModelExpression), + x => selector((TProp)x!)); var (disposable, obs) = BindToDirect(source, view, viewExpression); - return new ReactiveBinding(view, viewExpression, vmExpression, obs, BindingDirection.OneWay, disposable); + return new ReactiveBinding( + view, + viewExpression, + viewModelExpression, + obs, + BindingDirection.OneWay, + disposable); } /// - public IDisposable BindTo( + public IDisposable BindTo( + IObservable observedChange, + TTarget? target, + Expression> propertyExpression) + where TTarget : class => + BindTo(observedChange, target, propertyExpression, null, null); + + /// + public IDisposable BindTo( IObservable observedChange, TTarget? target, - Expression> propertyExpression, - object? conversionHint = null, - IBindingTypeConverter? vmToViewConverterOverride = null) + Expression> propertyExpression, + object? conversionHint) + where TTarget : class => + BindTo(observedChange, target, propertyExpression, conversionHint, null); + + /// + public IDisposable BindTo( + IObservable observedChange, + TTarget? target, + Expression> propertyExpression, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) where TTarget : class { ArgumentExceptionHelper.ThrowIfNull(target); @@ -439,61 +480,60 @@ public IDisposable BindTo( var viewExpression = Reflection.Rewrite(propertyExpression.Body); - var shouldBind = target is not IViewFor viewFor || _hookEvaluator.EvaluateBindingHooks(null, viewFor, null!, viewExpression, BindingDirection.OneWay); + var shouldBind = target is not IViewFor viewFor || + _hookEvaluator.EvaluateBindingHooks( + null, + viewFor, + null!, + viewExpression, + BindingDirection.OneWay); if (!shouldBind) { - return Disposable.Empty; + return EmptyDisposable.Instance; } - // First, try to find a registered converter (prioritize user-registered converters) - var converterObj = vmToViewConverterOverride ?? GetConverterForTypes(typeof(TValue), typeof(TTValue?)); + var converterObj = viewModelToViewConverterOverride ?? GetConverterForTypes(typeof(TValue), typeof(TTargetValue?)); if (converterObj is not null) { - // Use the converter - var source = - observedChange.SelectMany(x => + var source = new ChooseObservable( + observedChange, + x => { - // Try converter first var convertResult = BindingTypeConverterDispatch.TryConvertAny( converterObj, typeof(TValue), x, - typeof(TTValue?), + typeof(TTargetValue?), conversionHint, out var tmp); if (convertResult) { - return Observable.Return(tmp); + return (true, tmp); } - // Converter failed - // If no override was provided (auto-discovered converter), try direct assignment - if (vmToViewConverterOverride is null && typeof(TTValue).IsAssignableFrom(typeof(TValue))) + if (viewModelToViewConverterOverride is null && typeof(TTargetValue).IsAssignableFrom(typeof(TValue))) { - return Observable.Return((object?)x); + return (true, (object?)x); } - // Cannot convert - skip this update - return Observable.Empty; + return (false, null); }); - var (disposable, _) = BindToDirect(source, target!, viewExpression); + var (disposable, _) = BindToDirect(source, target, viewExpression); return disposable; } - // No converter found - check if types are directly assignable (includes same type and compatible reference types) - if (typeof(TTValue).IsAssignableFrom(typeof(TValue))) + if (typeof(TTargetValue).IsAssignableFrom(typeof(TValue))) { - // No conversion needed - direct assignment - var source = observedChange.Select(x => (object?)x); - var (disposable, _) = BindToDirect(source, target!, viewExpression); + var source = new SelectObservable(observedChange, static x => x); + var (disposable, _) = BindToDirect(source, target, viewExpression); return disposable; } - // No converter and types not assignable - throw exception - throw new ArgumentException($"Can't convert {typeof(TValue)} to {typeof(TTValue)}. To fix this, register a IBindingTypeConverter"); + throw new ArgumentException( + $"Can't convert {typeof(TValue)} to {typeof(TTargetValue)}. To fix this, register a IBindingTypeConverter"); } /// @@ -517,31 +557,6 @@ public IDisposable BindTo( internal static object? GetConverterForTypes(Type lhs, Type rhs) => _staticConverterResolver.GetBindingConverter(lhs, rhs); - /// - /// Schedules a binding notification before a view value is read or written. - /// - /// The view type. - /// The view participating in the binding. - /// The binding notification value. - /// An observable that emits when the binding should continue. - internal virtual IObservable ScheduleForBinding(TView view, bool value) - where TView : class => - Observable.Return(value); - - /// - /// Sets a value on the view. - /// - /// The view type. - /// The view being updated. - /// The value setter to invoke. - internal virtual void SetViewValue(TView view, Action setter) - where TView : class - { - ArgumentExceptionHelper.ThrowIfNull(setter); - - setter(); - } - /// /// Binds an observable to a target member directly using compiled accessors. /// @@ -555,6 +570,10 @@ internal virtual void SetViewValue(TView view, Action setter) /// A tuple containing the subscription and an observable sequence of values that were effectively set. /// /// Thrown when a required getter cannot be resolved. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] private (IDisposable disposable, IObservable value) BindToDirect( IObservable changeObservable, TTarget target, @@ -568,14 +587,27 @@ internal virtual void SetViewValue(TView view, Action setter) var memberInfo = viewExpression.GetMemberInfo(); var setter = Reflection.GetValueSetterOrThrow(memberInfo); - var getter = Reflection.GetValueFetcherOrThrow(memberInfo) ?? throw new InvalidOperationException("getter was not found."); + var getter = Reflection.GetValueFetcherOrThrow(memberInfo) ?? + throw new InvalidOperationException("getter was not found."); var setObservableWithEmit = _expressionCompiler.IsDirectMemberAccess(viewExpression) - ? _expressionCompiler.CreateDirectSetObservable(target, changeObservable, viewExpression, getter, setter, _converterResolver.GetSetMethodConverter) - : _expressionCompiler.CreateChainedSetObservable(target, changeObservable, viewExpression, _expressionCompiler.GetExpressionChainArray(viewExpression.GetParent()!) ?? [], getter, setter, _converterResolver.GetSetMethodConverter); - - var setObservable = setObservableWithEmit.Select(x => x.Value); + ? _expressionCompiler.CreateDirectSetObservable( + target, + changeObservable, + viewExpression, + getter, + setter, + _converterResolver.GetSetMethodConverter) : _expressionCompiler.CreateChainedSetObservable( + target, + changeObservable, + viewExpression, + _expressionCompiler.GetExpressionChainArray(viewExpression.GetParent()!) ?? [], + getter, + setter, + _converterResolver.GetSetMethodConverter); + + var setObservable = new SelectObservable<(bool ShouldEmit, TValue Value), TValue>(setObservableWithEmit, static x => x.Value); var subscription = SubscribeWithBindingErrorHandling(setObservable, viewExpression); return (subscription, setObservable); @@ -591,13 +623,15 @@ internal virtual void SetViewValue(TView view, Action setter) /// /// Thrown when the binding receives an exception with an inner exception, matching legacy behavior. /// - private IDisposable SubscribeWithBindingErrorHandling(IObservable setObservable, Expression viewExpression) + private IDisposable SubscribeWithBindingErrorHandling( + IObservable setObservable, + Expression viewExpression) { ArgumentExceptionHelper.ThrowIfNull(setObservable); ArgumentExceptionHelper.ThrowIfNull(viewExpression); - return setObservable.Subscribe( - _ => { }, + return setObservable.Subscribe(new DelegateObserver( + static _ => { }, ex => { this.Log().Error(ex, $"{viewExpression} Binding received an Exception!"); @@ -606,128 +640,400 @@ private IDisposable SubscribeWithBindingErrorHandling(IObservable BindImpl( - TViewModel? viewModel, - TView view, - Expression> vmProperty, - Expression> viewProperty, - IObservable? signalViewUpdate, - OutFunc vmToViewConverter, - OutFunc viewToVmConverter, - TriggerUpdate triggerUpdate = TriggerUpdate.ViewToViewModel) + /// + /// Core two-way binding implementation that wires up view-model-to-view and view-to-view-model change pipelines. + /// + /// The type of the view model. + /// The type of the view. + /// The type of the view model property. + /// The type of the view property. + /// A dummy type used only to signal view updates. + /// The bundled inputs describing the two-way binding to create. + /// The configured two-way reactive binding, or null if hooks blocked the binding. + private ReactiveBinding BindImpl< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>(in TwoWayBindRequest request) where TViewModel : class where TView : class, IViewFor { - ArgumentExceptionHelper.ThrowIfNull(vmProperty); - ArgumentExceptionHelper.ThrowIfNull(viewProperty); + ArgumentExceptionHelper.ThrowIfNull(request.ViewModelProperty); + ArgumentExceptionHelper.ThrowIfNull(request.ViewProperty); - var signalInitialUpdate = new Subject(); - var vmExpression = Reflection.Rewrite(vmProperty.Body); - var viewExpression = Reflection.Rewrite(viewProperty.Body); + var view = request.View; + BroadcastSubject signalInitialUpdate = new(); + var viewModelExpression = Reflection.Rewrite(request.ViewModelProperty.Body); + var viewExpression = Reflection.Rewrite(request.ViewProperty.Body); - // Pre-compile expression chains ONCE at binding setup time. - // This is the "reflection boundary". - Expression[] vmExpressionChainArray = [.. vmExpression.GetExpressionChain()]; + Expression[] viewModelExpressionChainArray = [.. viewModelExpression.GetExpressionChain()]; Expression[] viewExpressionChainArray = [.. viewExpression.GetExpressionChain()]; - // VM chain expects root = view.ViewModel (object?). - var vmChainGetter = new Reflection.CompiledPropertyChain(vmExpressionChainArray); + Reflection.CompiledPropertyChain viewModelChainGetter = new(viewModelExpressionChainArray); + Reflection.CompiledPropertyChain viewChainGetter = new(viewExpressionChainArray); + Reflection.CompiledPropertyChainSetter viewChainSetter = new(viewExpressionChainArray); + Reflection.CompiledPropertyChainSetter viewModelChainSetter = new(viewModelExpressionChainArray); - // View chain expects root = view (TView). - var viewChainGetter = new Reflection.CompiledPropertyChain(viewExpressionChainArray); + var viewModelToViewConverter = request.ViewModelToViewConverter; + var viewToViewModelConverter = request.ViewToViewModelConverter; - // Setters for two-way binding. - var viewChainSetter = new Reflection.CompiledPropertyChainSetter(viewExpressionChainArray); - var vmChainSetter = new Reflection.CompiledPropertyChainSetter(vmExpressionChainArray); + var viewChanges = new SelectObservable( + view.WhenAnyDynamic(viewExpression, x => (TViewPropertyType?)x.Value), + static _ => false); - IObservable<(bool isValid, object? view, bool isViewModel)>? changeWithValues = null; + var somethingChanged = BuildChangeSource( + request.TriggerUpdate, + request.SignalViewUpdate, + request.ViewModel, + view, + viewModelExpression, + viewChanges, + signalInitialUpdate); - if (triggerUpdate == TriggerUpdate.ViewToViewModel) - { - var signalObservable = signalViewUpdate is not null - ? signalViewUpdate.Select(_ => false) - : view.WhenAnyDynamic(viewExpression, x => (TVProp?)x.Value).Select(_ => false); - - var somethingChanged = Observable.Merge( - Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Select(_ => true), - signalInitialUpdate.Select(_ => true), - signalObservable) - .SelectMany(value => ScheduleForBinding(view, value)); - - changeWithValues = somethingChanged.Select(isVm => - !vmChainGetter.TryGetValue(view.ViewModel, out TVMProp vmValue) || - !viewChainGetter.TryGetValue(view, out TVProp vValue) - ? (false, null, false) - : isVm - ? !vmToViewConverter(vmValue, out var vmAsView) || EqualityComparer.Default.Equals(vValue, vmAsView) - ? (false, null, false) - : (true, vmAsView, isVm) - : !viewToVmConverter(vValue, out var vAsViewModel) || EqualityComparer.Default.Equals(vmValue, vAsViewModel) - ? (false, null, false) - : (true, vAsViewModel, isVm)); - } - else - { - var somethingChanged = Observable.Merge( - signalViewUpdate is null - ? Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Select(_ => true) - : signalViewUpdate.Select(_ => true) - .Merge(Reflection.ViewModelWhenAnyValue(viewModel, view, vmExpression).Select(_ => true).Take(1)), - signalInitialUpdate.Select(_ => true), - view.WhenAnyDynamic(viewExpression, x => (TVProp?)x.Value).Select(_ => false)) - .SelectMany(value => ScheduleForBinding(view, value)); - - changeWithValues = somethingChanged.Select(isVm => - !vmChainGetter.TryGetValue(view.ViewModel, out TVMProp vmValue) || - !viewChainGetter.TryGetValue(view, out TVProp vValue) - ? (false, null, false) - : isVm - ? !vmToViewConverter(vmValue, out var vmAsView) || EqualityComparer.Default.Equals(vValue, vmAsView) - ? (false, null, false) - : (true, vmAsView, isVm) - : !viewToVmConverter(vValue, out var vAsViewModel) || EqualityComparer.Default.Equals(vmValue, vAsViewModel) - ? (false, null, false) - : (true, vAsViewModel, isVm)); - } + var changeWithValues = new SelectObservable( + somethingChanged, + isViewModelChange => + ProjectChange(isViewModelChange, view, viewModelChainGetter, viewChainGetter, viewModelToViewConverter, viewToViewModelConverter)); - var ret = _hookEvaluator.EvaluateBindingHooks(viewModel, view, vmExpression, viewExpression, BindingDirection.TwoWay); + var ret = _hookEvaluator.EvaluateBindingHooks( + request.ViewModel, + view, + viewModelExpression, + viewExpression, + BindingDirection.TwoWay); if (!ret) { return null!; } - var changes = - changeWithValues - .Where(value => value.isValid) - .Select(value => (value.view, value.isViewModel)) - .Publish() - .RefCount(); + // Filter to valid changes and project to the (view, isViewModel) pair, then multicast through a shared subject + // (Publish + RefCount) so the internal setter and external subscribers share one upstream subscription. + var projected = new ChooseObservable<(bool isValid, object? view, bool isViewModel), (object? view, bool isViewModel)>( + changeWithValues, + value => value.isValid ? (true, (value.view, value.isViewModel)) : (false, default)); + + var changes = new BroadcastSubject<(object? view, bool isViewModel)>(); + var upstreamConnection = projected.Subscribe(changes); - var disposable = changes.Subscribe(isVmWithLatestValue => + var setterSubscription = changes.Subscribe(new DelegateObserver<(object? view, bool isViewModel)>(latestValue => { - if (isVmWithLatestValue.isViewModel) + if (latestValue.isViewModel) { - SetViewValue(view, () => viewChainSetter.TrySetValue(view, isVmWithLatestValue.view, false)); + viewChainSetter.TrySetValue(view, latestValue.view, false); } else { - vmChainSetter.TrySetValue(view.ViewModel, isVmWithLatestValue.view, false); + viewModelChainSetter.TrySetValue(view.ViewModel, latestValue.view, false); } - }); + })); - // NB: Even though it's technically a two-way bind, most people want the ViewModel to win at first. signalInitialUpdate.OnNext(true); - return new ReactiveBinding( + return new( view, viewExpression, - vmExpression, + viewModelExpression, changes, BindingDirection.TwoWay, - disposable); + new CompositeDisposable(upstreamConnection, setterSubscription)); + } + + /// Projects each value of a source through a selector. Specialised binding projection; no generic operator. + /// The source element type. + /// The projected element type. + /// The source observable. + /// Projects a source value into a result. + private sealed class SelectObservable(IObservable source, Func selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } + + /// Applies the selector to each value and forwards the result. + /// The observer receiving projected values. + /// Projects a source value into a result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver + { + /// + public void OnNext(TIn value) + { + TOut result; + try + { + result = selector(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// + /// For each source value, applies a chooser that either yields a value to forward or signals "skip". Fuses the + /// binding's convert-or-skip and filter-then-project pipelines into one specialised sink. + /// + /// The source element type. + /// The forwarded element type. + /// The source observable. + /// Maps a source value to (forward, value); when forward is false the value is skipped. + private sealed class ChooseObservable(IObservable source, Func chooser) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, chooser)); + } + + /// Applies the chooser to each value and forwards only the chosen ones. + /// The observer receiving chosen values. + /// Maps a source value to (forward, value). + private sealed class Sink(IObserver downstream, Func chooser) : IObserver + { + /// + public void OnNext(TIn value) + { + (bool HasValue, TOut Value) result; + try + { + result = chooser(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + if (!result.HasValue) + { + return; + } + + downstream.OnNext(result.Value); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// Forwards the values of every source and completes once all of them have completed. Specialised binding merge. + /// The element type. + /// The sources to merge. + private sealed class MergeObservable(IObservable[] sources) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, sources); + } + + /// Forwards every source value under a gate and completes once all sources complete. + private sealed class Sink : IDisposable + { + /// Guards downstream delivery and the completion counter. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving the merged values. + private readonly IObserver _downstream; + + /// The subscriptions to each source. + private readonly IDisposable?[] _subscriptions; + + /// The number of sources. + private readonly int _count; + + /// The number of sources that have completed. + private int _doneCount; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Initializes a new instance of the class and subscribes to every source. + /// The observer receiving the merged values. + /// The sources to merge. + public Sink(IObserver downstream, IObservable[] sources) + { + _downstream = downstream; + _count = sources.Length; + _subscriptions = new IDisposable?[sources.Length]; + for (var i = 0; i < sources.Length; i++) + { + _subscriptions[i] = sources[i].Subscribe(new Element(this)); + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Forwards one source value to the downstream. + /// The value to forward. + private void OnNextAt(T value) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _downstream.OnNext(value); + } + } + + /// Forwards an error from any source. + /// The error to forward. + private void OnErrorAt(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _downstream.OnError(error); + } + + /// Completes the downstream once every source has completed. + private void OnCompletedAt() + { + lock (_gate) + { + if (_stopped || ++_doneCount < _count) + { + return; + } + + _stopped = true; + } + + _downstream.OnCompleted(); + } + + /// Routes one source's notifications to the parent sink. + /// The owning sink. + private sealed class Element(Sink parent) : IObserver + { + /// + public void OnNext(T value) => parent.OnNextAt(value); + + /// + public void OnError(Exception error) => parent.OnErrorAt(error); + + /// + public void OnCompleted() => parent.OnCompletedAt(); + } + } + } + + /// Forwards the first value of a source then completes and unsubscribes. Specialised binding Take(1). + /// The element type. + /// The source observable. + private sealed class Take1Observable(IObservable source) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer); + return sink.Run(source); + } + + /// Forwards the first value, then completes the downstream and disposes the subscription. + private sealed class Sink(IObserver downstream) : IObserver, IDisposable + { + /// The subscription to the source. + private IDisposable? _subscription; + + /// Whether the first value has been delivered. + private bool _done; + + /// Subscribes to the source. + /// The source observable. + /// The sink, which disposes the run. + public Sink Run(IObservable source) + { + _subscription = source.Subscribe(this); + return this; + } + + /// + public void OnNext(T value) + { + if (_done) + { + return; + } + + _done = true; + downstream.OnNext(value); + downstream.OnCompleted(); + Dispose(); + } + + /// + public void OnError(Exception error) + { + if (_done) + { + return; + } + + downstream.OnError(error); + } + + /// + public void OnCompleted() + { + if (_done) + { + return; + } + + downstream.OnCompleted(); + } + + /// + public void Dispose() => _subscription?.Dispose(); + } } } diff --git a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs index 11d79ac277..c629019d61 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs @@ -1,8 +1,11 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reactive; + namespace ReactiveUI; /// @@ -31,10 +34,14 @@ namespace ReactiveUI; /// /// [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] -[RequiresDynamicCode("Uses dynamic binding paths which may require runtime code generation or reflection-based invocation.")] +[RequiresDynamicCode( + "Uses dynamic binding paths which may require runtime code generation or reflection-based invocation.")] public static class PropertyBindingMixins { - private static readonly PropertyBinderImplementation _defaultBinderImplementation; + /// + /// The binder implementation used by all extension methods in this class. + /// + private static readonly PropertyBinderImplementation _binderImplementation; /// /// Initializes static members of the class. @@ -42,21 +49,87 @@ public static class PropertyBindingMixins /// This static constructor is called automatically to perform type-level initialization before /// any static members are accessed or any static methods are invoked. It is not intended to be called directly by /// user code. - static PropertyBindingMixins() => _defaultBinderImplementation = new PropertyBinderImplementation(); + static PropertyBindingMixins() => _binderImplementation = new(); + + /// + /// Binds the specified view model property to the given view property. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding Bind( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty) + where TViewModel : class + where TView : class, IViewFor => + Bind(view, viewModel, viewModelProperty, viewProperty, null, null, null); + + /// + /// Binds the specified view model property to the given view property. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An object that can provide a hint for the converter. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding Bind( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint) + where TViewModel : class + where TView : class, IViewFor => + Bind(view, viewModel, viewModelProperty, viewProperty, conversionHint, null, null); - private static IPropertyBinderImplementation BinderImplementation => - AppLocator.Current.GetService() ?? _defaultBinderImplementation; + /// + /// Binds the specified view model property to the given view property. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from the view model to view property. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding Bind( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) + where TViewModel : class + where TView : class, IViewFor => + Bind(view, viewModel, viewModelProperty, viewProperty, conversionHint, viewModelToViewConverterOverride, null); /// /// Binds the specified view model property to the given view property. /// /// The type of the view model being bound. /// The type of the view being bound. - /// The type of the property bound on the view model. - /// The type of the property bound on the view. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. /// The instance of the view to bind. /// The instance of the view model to bind. - /// + /// /// An expression indicating the property that is bound on the view model. /// This can be a chain of properties of the form. vm => vm.Foo.Bar.Baz /// and the binder will attempt to subscribe to changes on each recursively. @@ -70,11 +143,11 @@ public static class PropertyBindingMixins /// An object that can provide a hint for the converter. /// The semantics of this object is defined by the converter used. /// - /// + /// /// An optional to use when converting from the /// viewModel to view property. /// - /// + /// /// An optional to use when converting from the /// view to viewModel property. /// @@ -82,25 +155,157 @@ public static class PropertyBindingMixins /// An instance of that, when disposed, /// disconnects the binding. /// - public static IReactiveBinding Bind( + public static IReactiveBinding Bind( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride, + IBindingTypeConverter? viewToViewModelConverterOverride) + where TViewModel : class + where TView : class, IViewFor => + _binderImplementation.Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + (IObservable?)null, + conversionHint, + viewModelToViewConverterOverride, + viewToViewModelConverterOverride); + + /// + /// Binds the specified view model property to the given view property with a custom view update signal. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type; only the fact that signalViewUpdate emits values is used. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An observable that, when signaled, indicates the view property has been changed. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding Bind< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate) + where TViewModel : class + where TView : class, IViewFor => + Bind(view, viewModel, viewModelProperty, viewProperty, signalViewUpdate, null, null, null, TriggerUpdate.ViewToViewModel); + + /// + /// Binds the specified view model property to the given view property with a custom view update signal. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type; only the fact that signalViewUpdate emits values is used. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An observable that, when signaled, indicates the view property has been changed. + /// An object that can provide a hint for the converter. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding Bind< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>( this TView view, TViewModel? viewModel, - Expression> vmProperty, - Expression> viewProperty, - object? conversionHint = null, - IBindingTypeConverter? vmToViewConverterOverride = null, - IBindingTypeConverter? viewToVMConverterOverride = null) + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint) + where TViewModel : class + where TView : class, IViewFor => + Bind(view, viewModel, viewModelProperty, viewProperty, signalViewUpdate, conversionHint, null, null, TriggerUpdate.ViewToViewModel); + + /// + /// Binds the specified view model property to the given view property with a custom view update signal. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type; only the fact that signalViewUpdate emits values is used. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An observable that, when signaled, indicates the view property has been changed. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from the view model to view property. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding Bind< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) + where TViewModel : class + where TView : class, IViewFor => + Bind(view, viewModel, viewModelProperty, viewProperty, signalViewUpdate, conversionHint, viewModelToViewConverterOverride, null, TriggerUpdate.ViewToViewModel); + + /// + /// Binds the specified view model property to the given view property with a custom view update signal. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type; only the fact that signalViewUpdate emits values is used. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An observable that, when signaled, indicates the view property has been changed. + /// An object that can provide a hint for the converter. + /// An optional converter to use when converting from the view model to view property. + /// An optional converter to use when converting from the view to view model property. + /// An instance of IDisposable that, when disposed, disconnects the binding. + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload is part of the public binding API surface; the parameter count is intentional.")] + public static IReactiveBinding Bind< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride, + IBindingTypeConverter? viewToViewModelConverterOverride) where TViewModel : class where TView : class, IViewFor => - BinderImplementation.Bind( - viewModel, - view, - vmProperty, - viewProperty, - (IObservable?)null, - conversionHint, - vmToViewConverterOverride, - viewToVMConverterOverride); + Bind(view, viewModel, viewModelProperty, viewProperty, signalViewUpdate, conversionHint, viewModelToViewConverterOverride, viewToViewModelConverterOverride, TriggerUpdate.ViewToViewModel); /// /// Binds the specified view model property to the given view property, and @@ -108,13 +313,13 @@ public static class PropertyBindingMixins /// /// The type of the view model being bound. /// The type of the view being bound. - /// The type of the property bound on the view model. - /// The type of the property bound on the view. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. /// A dummy type, only the fact that /// emits values is considered, not the actual values emitted. /// The instance of the view to bind. /// The instance of the view model to bind. - /// An expression indicating the property that is bound on the view model. + /// An expression indicating the property that is bound on the view model. /// This can be a chain of properties of the form. vm => vm.Foo.Bar.Baz /// and the binder will attempt to subscribe to changes on each recursively. /// The property on the view that is to be bound. @@ -125,39 +330,57 @@ public static class PropertyBindingMixins /// property accordingly. /// An object that can provide a hint for the converter. /// The semantics of this object is defined by the converter used. - /// An optional to use when converting from the + /// An optional to use when converting from the /// viewModel to view property. - /// An optional to use when converting from the + /// An optional to use when converting from the /// view to viewModel property. /// The trigger update direction. /// /// An instance of that, when disposed, /// disconnects the binding. /// - public static IReactiveBinding Bind( + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload is part of the public binding API surface; the parameter count is intentional.")] + public static IReactiveBinding Bind< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>( this TView view, TViewModel? viewModel, - Expression> vmProperty, - Expression> viewProperty, + Expression> viewModelProperty, + Expression> viewProperty, IObservable? signalViewUpdate, - object? conversionHint = null, - IBindingTypeConverter? vmToViewConverterOverride = null, - IBindingTypeConverter? viewToVMConverterOverride = null, - TriggerUpdate triggerUpdate = TriggerUpdate.ViewToViewModel) + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride, + IBindingTypeConverter? viewToViewModelConverterOverride, + TriggerUpdate triggerUpdate) where TViewModel : class where TView : class, IViewFor => - BinderImplementation.Bind(viewModel, view, vmProperty, viewProperty, signalViewUpdate, conversionHint, vmToViewConverterOverride, viewToVMConverterOverride, triggerUpdate); + _binderImplementation.Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + signalViewUpdate, + conversionHint, + viewModelToViewConverterOverride, + viewToViewModelConverterOverride, + triggerUpdate); /// /// Binds the specified view model property to the given view property. /// /// The type of the view model being bound. /// The type of the view being bound. - /// The type of the property bound on the view model. - /// The type of the property bound on the view. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. /// The instance of the view to bind. /// The instance of the view model to bind. - /// + /// /// An expression indicating the property that is bound on the view model. /// This can be a chain of properties of the form. vm => vm.Foo.Bar.Baz /// and the binder will attempt to subscribe to changes on each recursively. @@ -167,11 +390,11 @@ public static class PropertyBindingMixins /// This can be a chain of properties of the form. view => view.Foo.Bar.Baz /// and the binder will attempt to set the last one each time the view model property is updated. /// - /// + /// /// Delegate to convert the value of the view model's property's type to a value of the /// view's property's type. /// - /// + /// /// Delegate to convert the value of the view's property's type to a value of the /// view model's property's type. /// @@ -179,28 +402,68 @@ public static class PropertyBindingMixins /// An instance of that, when disposed, /// disconnects the binding. /// - public static IReactiveBinding Bind( + public static IReactiveBinding Bind( this TView view, TViewModel? viewModel, - Expression> vmProperty, - Expression> viewProperty, - Func vmToViewConverter, - Func viewToVmConverter) + Expression> viewModelProperty, + Expression> viewProperty, + Func viewModelToViewConverter, + Func viewToViewModelConverter) where TViewModel : class - where TView : class, IViewFor => BinderImplementation.Bind(viewModel, view, vmProperty, viewProperty, (IObservable?)null, vmToViewConverter, viewToVmConverter); + where TView : class, IViewFor => _binderImplementation.Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + (IObservable?)null, + viewModelToViewConverter, + viewToViewModelConverter); + + /// + /// Binds the specified view model property to the given view property using delegate converters. + /// + /// The type of the view model being bound. + /// The type of the view being bound. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. + /// A dummy type; only the fact that signalViewUpdate emits values is used. + /// The instance of the view to bind. + /// The instance of the view model to bind. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An observable that, when signaled, indicates the view property has been changed. + /// Delegate to convert a view model property value to a view property value. + /// Delegate to convert a view property value to a view model property value. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding Bind< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + IObservable? signalViewUpdate, + Func viewModelToViewConverter, + Func viewToViewModelConverter) + where TViewModel : class + where TView : class, IViewFor => + Bind(view, viewModel, viewModelProperty, viewProperty, signalViewUpdate, viewModelToViewConverter, viewToViewModelConverter, TriggerUpdate.ViewToViewModel); /// /// Binds the specified view model property to the given view property. /// /// The type of the view model being bound. /// The type of the view being bound. - /// The type of the property bound on the view model. - /// The type of the property bound on the view. + /// The type of the property bound on the view model. + /// The type of the property bound on the view. /// A dummy type, only the fact that /// emits values is considered, not the actual values emitted. /// The instance of the view to bind. /// The instance of the view model to bind. - /// An expression indicating the property that is bound on the view model. + /// An expression indicating the property that is bound on the view model. /// This can be a chain of properties of the form. vm => vm.Foo.Bar.Baz /// and the binder will attempt to subscribe to changes on each recursively. /// The property on the view that is to be bound. @@ -209,35 +472,96 @@ public static class PropertyBindingMixins /// An observable, that when signaled, indicates that the view property /// has been changed, and that the binding should update the view model /// property accordingly. - /// Delegate to convert the value of the view model's property's type to a value of the + /// Delegate to convert the value of the view model's property's type to a value of the /// view's property's type. - /// Delegate to convert the value of the view's property's type to a value of the + /// Delegate to convert the value of the view's property's type to a value of the /// view model's property's type. /// The trigger update direction. /// /// An instance of that, when disposed, /// disconnects the binding. /// - public static IReactiveBinding Bind( + [SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "This overload is part of the public binding API surface; the parameter count is intentional.")] + public static IReactiveBinding Bind< + TViewModel, + TView, + TViewModelPropertyType, + TViewPropertyType, + TDontCare>( this TView view, TViewModel? viewModel, - Expression> vmProperty, - Expression> viewProperty, + Expression> viewModelProperty, + Expression> viewProperty, IObservable? signalViewUpdate, - Func vmToViewConverter, - Func viewToVmConverter, - TriggerUpdate triggerUpdate = TriggerUpdate.ViewToViewModel) + Func viewModelToViewConverter, + Func viewToViewModelConverter, + TriggerUpdate triggerUpdate) where TViewModel : class where TView : class, IViewFor => - BinderImplementation.Bind(viewModel, view, vmProperty, viewProperty, signalViewUpdate, vmToViewConverter, viewToVmConverter, triggerUpdate); + _binderImplementation.Bind( + viewModel, + view, + viewModelProperty, + viewProperty, + signalViewUpdate, + viewModelToViewConverter, + viewToViewModelConverter, + triggerUpdate); + + /// + /// Binds the given property on the view model to a given property on the view in a one-way fashion. + /// + /// The type of the view model. + /// The type of the view. + /// The type of view model property. + /// The type of the property bound on the view. + /// The instance of the view object which is bound. + /// The view model that is bound. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding OneWayBind( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty) + where TViewModel : class + where TView : class, IViewFor => + OneWayBind(view, viewModel, viewModelProperty, viewProperty, null, null); + + /// + /// Binds the given property on the view model to a given property on the view in a one-way fashion. + /// + /// The type of the view model. + /// The type of the view. + /// The type of view model property. + /// The type of the property bound on the view. + /// The instance of the view object which is bound. + /// The view model that is bound. + /// An expression indicating the property that is bound on the view model. + /// The property on the view that is to be bound. + /// An object that can provide a hint for the converter. + /// An instance of IDisposable that, when disposed, disconnects the binding. + public static IReactiveBinding OneWayBind( + this TView view, + TViewModel? viewModel, + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint) + where TViewModel : class + where TView : class, IViewFor => + OneWayBind(view, viewModel, viewModelProperty, viewProperty, conversionHint, null); /// /// Binds the given property on the view model to a given property on the view in a one-way (view model to view) fashion. /// /// The type of the view model. /// The type of the view. - /// The type of view model property. - /// The type of the property bound on the view. + /// The type of view model property. + /// The type of the property bound on the view. /// /// The instance of the view object which is bound. Usually, it is the. this /// instance. @@ -245,7 +569,7 @@ public static class PropertyBindingMixins /// /// The view model that is bound. /// It is usually set to the property of the . - /// + /// /// An expression indicating the property that is bound on the view model. /// This can be a chain of properties of the form. vm => vm.Foo.Bar.Baz /// and the binder will attempt to subscribe to changes on each recursively. @@ -259,7 +583,7 @@ public static class PropertyBindingMixins /// An object that can provide a hint for the converter. /// The semantics of this object is defined by the converter used. /// - /// + /// /// An optional to use when converting from the /// viewModel to view property. /// @@ -267,22 +591,22 @@ public static class PropertyBindingMixins /// An instance of that, when disposed, /// disconnects the binding. /// - public static IReactiveBinding OneWayBind( + public static IReactiveBinding OneWayBind( this TView view, TViewModel? viewModel, - Expression> vmProperty, - Expression> viewProperty, - object? conversionHint = null, - IBindingTypeConverter? vmToViewConverterOverride = null) + Expression> viewModelProperty, + Expression> viewProperty, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) where TViewModel : class where TView : class, IViewFor => - BinderImplementation.OneWayBind( - viewModel, - view, - vmProperty, - viewProperty, - conversionHint, - vmToViewConverterOverride); + _binderImplementation.OneWayBind( + viewModel, + view, + viewModelProperty, + viewProperty, + conversionHint, + viewModelToViewConverterOverride); /// /// Binds the specified view model property to the given view, in a one-way (view model to view) fashion, @@ -294,7 +618,7 @@ public static IReactiveBinding OneWayBindThe return type of the . /// The instance of the view to bind to. /// The instance of the view model to bind to. - /// + /// /// An expression representing the property to be bound to on the view model. /// This can be a child property, for example x => x.Foo.Bar.Baz in which case /// the binding will attempt to subscribe recursively to updates in order to @@ -317,12 +641,70 @@ public static IReactiveBinding OneWayBind OneWayBind( this TView view, TViewModel? viewModel, - Expression> vmProperty, + Expression> viewModelProperty, Expression> viewProperty, Func selector) where TViewModel : class where TView : class, IViewFor => - BinderImplementation.OneWayBind(viewModel, view, vmProperty, viewProperty, selector); + _binderImplementation.OneWayBind(viewModel, view, viewModelProperty, viewProperty, selector); + + /// + /// BindTo takes an Observable stream and applies it to a target property. + /// + /// The source type. + /// The target object type. + /// The type of the property on the target object. + /// The observable stream to bind to a target property. + /// The target object whose property will be set. + /// An expression representing the target property to set. + /// An object that when disposed, disconnects the binding. + public static IDisposable BindTo( + this IObservable @this, + TTarget? target, + Expression> property) + where TTarget : class => + BindTo(@this, target, property, null, null); + + /// + /// BindTo takes an Observable stream and applies it to a target property. + /// + /// The source type. + /// The target object type. + /// The type of the property on the target object. + /// The observable stream to bind to a target property. + /// The target object whose property will be set. + /// An expression representing the target property to set. + /// An object that can provide a hint for the converter. + /// An object that when disposed, disconnects the binding. + public static IDisposable BindTo( + this IObservable @this, + TTarget? target, + Expression> property, + object? conversionHint) + where TTarget : class => + BindTo(@this, target, property, conversionHint, null); + + /// + /// BindTo takes an Observable stream and applies it to a target property. + /// + /// The source type. + /// The target object type. + /// The type of the property on the target object. + /// The observable stream to bind to a target property. + /// The target object whose property will be set. + /// An expression representing the target property to set. + /// + /// An optional to use when converting from the + /// viewModel to view property. + /// + /// An object that when disposed, disconnects the binding. + public static IDisposable BindTo( + this IObservable @this, + TTarget? target, + Expression> property, + IBindingTypeConverter? vmToViewConverterOverride) + where TTarget : class => + BindTo(@this, target, property, null, vmToViewConverterOverride); /// /// BindTo takes an Observable stream and applies it to a target @@ -332,7 +714,7 @@ public static IReactiveBinding OneWayBind /// The source type. /// The target object type. - /// The type of the property on the target object. + /// The type of the property on the target object. /// The observable stream to bind to a target property. /// The target object whose property will be set. /// @@ -343,17 +725,17 @@ public static IReactiveBinding OneWayBind - /// + /// /// An optional to use when converting from the /// viewModel to view property. /// /// An object that when disposed, disconnects the binding. - public static IDisposable BindTo( + public static IDisposable BindTo( this IObservable @this, TTarget? target, - Expression> property, - object? conversionHint = null, - IBindingTypeConverter? vmToViewConverterOverride = null) + Expression> property, + object? conversionHint, + IBindingTypeConverter? viewModelToViewConverterOverride) where TTarget : class => - BinderImplementation.BindTo(@this, target, property, conversionHint, vmToViewConverterOverride); + _binderImplementation.BindTo(@this, target, property, conversionHint, viewModelToViewConverterOverride); } diff --git a/src/ReactiveUI/Bindings/Property/TriggerUpdate.cs b/src/ReactiveUI/Bindings/Property/TriggerUpdate.cs index d3f3fecd7a..adee490b98 100644 --- a/src/ReactiveUI/Bindings/Property/TriggerUpdate.cs +++ b/src/ReactiveUI/Bindings/Property/TriggerUpdate.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Bindings/Reactive/IReactiveBinding.cs b/src/ReactiveUI/Bindings/Reactive/IReactiveBinding.cs index 331a3c51d8..9becc21d2c 100644 --- a/src/ReactiveUI/Bindings/Reactive/IReactiveBinding.cs +++ b/src/ReactiveUI/Bindings/Reactive/IReactiveBinding.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/Bindings/Reactive/ReactiveBinding.cs b/src/ReactiveUI/Bindings/Reactive/ReactiveBinding.cs index 17d431d842..deccc79d9a 100644 --- a/src/ReactiveUI/Bindings/Reactive/ReactiveBinding.cs +++ b/src/ReactiveUI/Bindings/Reactive/ReactiveBinding.cs @@ -1,10 +1,17 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; +/// +/// Represents an active binding between a view and a view model property, tracking direction and change notifications. +/// +/// The type of the view. +/// The type of the bound value. internal class ReactiveBinding( TView view, Expression viewExpression, @@ -42,9 +49,11 @@ public void Dispose() /// If we are disposing managed resources. protected virtual void Dispose(bool isDisposing) { - if (isDisposing) + if (!isDisposing) { - bindingDisposable.Dispose(); + return; } + + bindingDisposable.Dispose(); } } diff --git a/src/ReactiveUI/Builder/IReactiveUIBuilder.cs b/src/ReactiveUI/Builder/IReactiveUIBuilder.cs index f29dfa4b55..1ce841e20a 100644 --- a/src/ReactiveUI/Builder/IReactiveUIBuilder.cs +++ b/src/ReactiveUI/Builder/IReactiveUIBuilder.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; using System.Reflection; - using Splat.Builder; namespace ReactiveUI.Builder; @@ -78,7 +78,9 @@ public interface IReactiveUIBuilder : IAppBuilder /// The main thread scheduler. /// The platform services. /// The builder instance for chaining. - IReactiveUIBuilder ForCustomPlatform(IScheduler mainThreadScheduler, Action platformServices); + IReactiveUIBuilder ForCustomPlatform( + IScheduler mainThreadScheduler, + Action platformServices); /// /// Fors the platforms. @@ -93,6 +95,10 @@ public interface IReactiveUIBuilder : IAppBuilder /// The type of the view. /// The type of the view model. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IReactiveUIBuilder RegisterSingletonView() where TView : class, IViewFor, new() where TViewModel : class, IReactiveObject; @@ -102,8 +108,13 @@ IReactiveUIBuilder RegisterSingletonView() /// /// The type of the view model. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] #if NET6_0_OR_GREATER - IReactiveUIBuilder RegisterSingletonViewModel<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TViewModel>() + IReactiveUIBuilder RegisterSingletonViewModel< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TViewModel>() #else IReactiveUIBuilder RegisterSingletonViewModel() #endif @@ -115,6 +126,10 @@ IReactiveUIBuilder RegisterSingletonViewModel() /// The type of the view. /// The type of the view model. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IReactiveUIBuilder RegisterView() where TView : class, IViewFor, new() where TViewModel : class, IReactiveObject; @@ -124,6 +139,10 @@ IReactiveUIBuilder RegisterView() /// /// The type of the view model. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IReactiveUIBuilder RegisterViewModel() where TViewModel : class, IReactiveObject, new(); @@ -136,9 +155,22 @@ IReactiveUIBuilder RegisterViewModel() /// The type of the view model to register. Must be a reference type that implements IReactiveObject and has a /// parameterless constructor. /// The current instance of the reactive UI builder, enabling method chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IReactiveUIBuilder RegisterConstantViewModel() where TViewModel : class, IReactiveObject, new(); + /// + /// Withes the main thread scheduler. + /// + /// The scheduler. + /// + /// The builder instance for chaining. + /// + IReactiveUIBuilder WithMainThreadScheduler(IScheduler scheduler); + /// /// Withes the main thread scheduler. /// @@ -147,13 +179,17 @@ IReactiveUIBuilder RegisterConstantViewModel() /// /// The builder instance for chaining. /// - IReactiveUIBuilder WithMainThreadScheduler(IScheduler scheduler, bool setRxApp = true); + IReactiveUIBuilder WithMainThreadScheduler(IScheduler scheduler, bool setRxApp); /// /// Withes the platform module. /// /// The type of the registration module that implements IWantsToRegisterStuff. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IReactiveUIBuilder WithPlatformModule() where T : IWantsToRegisterStuff, new(); @@ -177,6 +213,15 @@ IReactiveUIBuilder WithPlatformModule() /// The builder instance for chaining. IReactiveUIBuilder WithRegistrationOnBuild(Action configureAction); + /// + /// Withes the task pool scheduler. + /// + /// The scheduler. + /// + /// The builder instance for chaining. + /// + IReactiveUIBuilder WithTaskPoolScheduler(IScheduler scheduler); + /// /// Withes the task pool scheduler. /// @@ -185,7 +230,7 @@ IReactiveUIBuilder WithPlatformModule() /// /// The builder instance for chaining. /// - IReactiveUIBuilder WithTaskPoolScheduler(IScheduler scheduler, bool setRxApp = true); + IReactiveUIBuilder WithTaskPoolScheduler(IScheduler scheduler, bool setRxApp); /// /// Configures a custom exception handler for unhandled errors in ReactiveUI observables. @@ -208,6 +253,10 @@ IReactiveUIBuilder WithPlatformModule() /// /// The type of the application state to manage. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IReactiveUIBuilder WithSuspensionHost(); /// @@ -291,7 +340,8 @@ IReactiveUIBuilder WithPlatformModule() /// /// The assembly. /// The builder instance for chaining. - [RequiresUnreferencedCode("Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to RegisterView explicitly.")] + [RequiresUnreferencedCode( + "Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to RegisterView explicitly.")] IReactiveUIBuilder WithViewsFromAssembly(Assembly assembly); /// @@ -409,7 +459,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The eighth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves nine instances and passes them to the action. @@ -425,7 +476,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The ninth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves ten instances and passes them to the action. @@ -442,7 +494,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The tenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves eleven instances and passes them to the action. @@ -460,7 +513,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The eleventh type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves twelve instances and passes them to the action. @@ -479,7 +533,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The twelfth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves thirteen instances and passes them to the action. @@ -499,7 +554,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The thirteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves fourteen instances and passes them to the action. @@ -520,7 +576,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The fourteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves fifteen instances and passes them to the action. @@ -542,7 +599,8 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The fifteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); /// /// Resolves sixteen instances and passes them to the action. @@ -565,5 +623,6 @@ IReactiveUIBuilder UsingSplatModule(T registrationModule) /// The sixteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - IReactiveUIInstance WithInstance(Action action); + IReactiveUIInstance WithInstance( + Action action); } diff --git a/src/ReactiveUI/Builder/IReactiveUIInstance.cs b/src/ReactiveUI/Builder/IReactiveUIInstance.cs index 22d64d1639..ef79748c3c 100644 --- a/src/ReactiveUI/Builder/IReactiveUIInstance.cs +++ b/src/ReactiveUI/Builder/IReactiveUIInstance.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; using Splat.Builder; namespace ReactiveUI.Builder; diff --git a/src/ReactiveUI/Builder/ReactiveUIBuilder.cs b/src/ReactiveUI/Builder/ReactiveUIBuilder.cs index 6aa344bb5a..98d75cf12f 100644 --- a/src/ReactiveUI/Builder/ReactiveUIBuilder.cs +++ b/src/ReactiveUI/Builder/ReactiveUIBuilder.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; using System.Reflection; - using Splat.Builder; namespace ReactiveUI.Builder; @@ -15,16 +15,46 @@ namespace ReactiveUI.Builder; /// public sealed class ReactiveUIBuilder : AppBuilder, IReactiveUIBuilder, IReactiveUIInstance { +#if ANDROID || IOS + /// The default small cache limit for mobile platforms. + private const int DefaultSmallCacheLimit = 32; + + /// The default big cache limit for mobile platforms. + private const int DefaultBigCacheLimit = 64; +#else + /// The default small cache limit for desktop platforms. + private const int DefaultSmallCacheLimit = 64; + + /// The default big cache limit for desktop platforms. + private const int DefaultBigCacheLimit = 256; +#endif + + /// Tracks whether platform registrations have been applied. private bool _platformRegistered; + + /// Tracks whether core registrations have been applied. private bool _coreRegistered; + + /// Whether to assign the main-thread scheduler to RxApp after build. private bool _setRxAppMainScheduler; + + /// Whether to assign the task-pool scheduler to RxApp after build. private bool _setRxAppTaskPoolScheduler; + + /// Optional custom exception handler for unhandled ReactiveUI errors. private IObserver? _exceptionHandler; + + /// Optional suspension host for application lifecycle management. private ISuspensionHost? _suspensionHost; + + /// Optional override for the small memoizing-cache capacity. private int? _smallCacheLimit; + + /// Optional override for the big memoizing-cache capacity. private int? _bigCacheLimit; + + /// Optional message bus instance registered during build. private IMessageBus? _messageBus; - private bool _reactiveUiInitialized; /// /// Initializes a new instance of the class. @@ -37,7 +67,6 @@ public ReactiveUIBuilder(IMutableDependencyResolver resolver, IReadonlyDependenc { CurrentMutable.InitializeSplat(); - // Register the ConverterService instance so it's accessible to registrations CurrentMutable.RegisterConstant(() => ConverterService); } @@ -70,6 +99,16 @@ public ReactiveUIBuilder(IMutableDependencyResolver resolver, IReadonlyDependenc /// public ConverterService ConverterService { get; } = new(); + /// + /// Configures the main thread scheduler for ReactiveUI. + /// + /// The main thread scheduler to use. + /// + /// The builder instance for chaining. + /// + public IReactiveUIBuilder WithMainThreadScheduler(IScheduler scheduler) => + WithMainThreadScheduler(scheduler, true); + /// /// Configures the main thread scheduler for ReactiveUI. /// @@ -78,13 +117,23 @@ public ReactiveUIBuilder(IMutableDependencyResolver resolver, IReadonlyDependenc /// /// The builder instance for chaining. /// - public IReactiveUIBuilder WithMainThreadScheduler(IScheduler scheduler, bool setRxApp = true) + public IReactiveUIBuilder WithMainThreadScheduler(IScheduler scheduler, bool setRxApp) { _setRxAppMainScheduler = setRxApp; MainThreadScheduler = scheduler; return this; } + /// + /// Configures the task pool scheduler for ReactiveUI. + /// + /// The task pool scheduler to use. + /// + /// The builder instance for chaining. + /// + public IReactiveUIBuilder WithTaskPoolScheduler(IScheduler scheduler) => + WithTaskPoolScheduler(scheduler, true); + /// /// Configures the task pool scheduler for ReactiveUI. /// @@ -93,7 +142,7 @@ public IReactiveUIBuilder WithMainThreadScheduler(IScheduler scheduler, bool set /// /// The builder instance for chaining. /// - public IReactiveUIBuilder WithTaskPoolScheduler(IScheduler scheduler, bool setRxApp = true) + public IReactiveUIBuilder WithTaskPoolScheduler(IScheduler scheduler, bool setRxApp) { _setRxAppTaskPoolScheduler = setRxApp; TaskpoolScheduler = scheduler; @@ -138,7 +187,7 @@ public IReactiveUIBuilder WithRegistration(IWantsToRegisterStuff registration) { ArgumentExceptionHelper.ThrowIfNull(registration); - var registrar = new DependencyResolverRegistrar(CurrentMutable); + DependencyResolverRegistrar registrar = new(CurrentMutable); registration.Register(registrar); return this; @@ -155,7 +204,6 @@ public IReactiveUIBuilder WithPlatformServices() return this; } - // Immediately register the platform ReactiveUI services into the provided resolver. WithPlatformModule(); _platformRegistered = true; @@ -170,15 +218,12 @@ public override IAppBuilder WithCoreServices() { if (!_coreRegistered) { - // Register all standard converters to the ConverterService RegisterStandardConverters(); - // Immediately register the core ReactiveUI services into the provided resolver (Splat). WithPlatformModule(); _coreRegistered = true; } - // Configure schedulers if specified ConfigureSchedulers(); return this; @@ -189,12 +234,12 @@ public override IAppBuilder WithCoreServices() /// /// The assembly to scan for views. /// The builder instance for method chaining. - [RequiresUnreferencedCode("Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to RegisterView explicitly.")] + [RequiresUnreferencedCode( + "Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to RegisterView explicitly.")] public IReactiveUIBuilder WithViewsFromAssembly(Assembly assembly) { ArgumentExceptionHelper.ThrowIfNull(assembly); - // Register views immediately against the builder's resolver CurrentMutable.RegisterViewsForViewModels(assembly); return this; } @@ -204,11 +249,15 @@ public IReactiveUIBuilder WithViewsFromAssembly(Assembly assembly) /// /// The type of the registration module that implements IWantsToRegisterStuff. /// The builder instance for method chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IReactiveUIBuilder WithPlatformModule() where T : IWantsToRegisterStuff, new() { - var registration = new T(); - var registrar = new DependencyResolverRegistrar(CurrentMutable); + T registration = new(); + DependencyResolverRegistrar registrar = new(CurrentMutable); registration.Register(registrar); return this; } @@ -250,7 +299,7 @@ public IReactiveUIBuilder UsingSplatBuilder(Action appBuilder) public IReactiveUIBuilder ForCustomPlatform( IScheduler mainThreadScheduler, Action platformServices) => - WithMainThreadScheduler(mainThreadScheduler) + WithMainThreadScheduler(mainThreadScheduler) .WithRegistrationOnBuild(platformServices); /// @@ -319,7 +368,7 @@ public IReactiveUIBuilder ConfigureViewLocator(Action config WithRegistrationOnBuild(resolver => resolver.Register(() => { - var viewLocator = new DefaultViewLocator(); + DefaultViewLocator viewLocator = new(); configure(viewLocator); return viewLocator; })); @@ -333,10 +382,12 @@ public IReactiveUIBuilder ConfigureSuspensionDriver(Action co WithRegistrationOnBuild(resolver => { var currentDriver = Current?.GetService(); - if (currentDriver != null) + if (currentDriver == null) { - configure(currentDriver); + return; } + + configure(currentDriver); }); /// @@ -544,9 +595,7 @@ public IReactiveUIBuilder WithConvertersFrom(IReadonlyDependencyResolver resolve { ArgumentExceptionHelper.ThrowIfNull(resolver); - // Import typed converters - var typedConverters = resolver.GetServices(); - foreach (var converter in typedConverters) + foreach (var converter in resolver.GetServices()) { if (converter is not null) { @@ -554,9 +603,7 @@ public IReactiveUIBuilder WithConvertersFrom(IReadonlyDependencyResolver resolve } } - // Import fallback converters - var fallbackConverters = resolver.GetServices(); - foreach (var converter in fallbackConverters) + foreach (var converter in resolver.GetServices()) { if (converter is not null) { @@ -564,9 +611,7 @@ public IReactiveUIBuilder WithConvertersFrom(IReadonlyDependencyResolver resolve } } - // Import set-method converters - var setMethodConverters = resolver.GetServices(); - foreach (var converter in setMethodConverters) + foreach (var converter in resolver.GetServices()) { if (converter is not null) { @@ -604,6 +649,10 @@ public IReactiveUIBuilder WithSuspensionHost() /// /// The type of the application state to manage. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IReactiveUIBuilder WithSuspensionHost() { _suspensionHost = new SuspensionHost(); @@ -639,9 +688,13 @@ public IReactiveUIBuilder WithCacheSizes(int smallCacheLimit, int bigCacheLimit) /// /// The view model type. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IReactiveUIBuilder RegisterViewModel() where TViewModel : class, IReactiveObject, new() => - WithRegistration(static resolver => resolver.Register(static () => new())); + WithRegistration(static resolver => resolver.Register(static () => new())); /// /// Registers a constant instance of the specified view model type in the dependency resolver. @@ -651,22 +704,31 @@ public IReactiveUIBuilder RegisterViewModel() /// The type of the view model to register. Must be a class that implements IReactiveObject and has a parameterless /// constructor. /// The current builder instance, enabling further configuration of the dependency resolver. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IReactiveUIBuilder RegisterConstantViewModel() where TViewModel : class, IReactiveObject, new() => - WithRegistration(static resolver => resolver.RegisterConstant(new TViewModel())); + WithRegistration(static resolver => resolver.RegisterConstant(new TViewModel())); /// /// Registers a custom view model with the dependency resolver. /// /// The view model type. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] #if NET6_0_OR_GREATER - public IReactiveUIBuilder RegisterSingletonViewModel<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TViewModel>() + public IReactiveUIBuilder RegisterSingletonViewModel< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TViewModel>() #else public IReactiveUIBuilder RegisterSingletonViewModel() #endif where TViewModel : class, IReactiveObject, new() => - WithRegistration(static resolver => resolver.RegisterLazySingleton(static () => new())); + WithRegistration(static resolver => resolver.RegisterLazySingleton(static () => new())); /// /// Registers a custom view for a specific view model. @@ -674,10 +736,14 @@ public IReactiveUIBuilder RegisterSingletonViewModel() /// The view type. /// The view model type. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IReactiveUIBuilder RegisterView() where TView : class, IViewFor, new() where TViewModel : class, IReactiveObject => - WithRegistration(static resolver => resolver.Register>(static () => new TView())); + WithRegistration(static resolver => resolver.Register>(static () => new TView())); /// /// Registers a custom view for a specific view model. @@ -685,10 +751,15 @@ public IReactiveUIBuilder RegisterView() /// The view type. /// The view model type. /// The builder instance for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public IReactiveUIBuilder RegisterSingletonView() where TView : class, IViewFor, new() where TViewModel : class, IReactiveObject => - WithRegistration(static resolver => resolver.RegisterLazySingleton>(static () => new TView())); + WithRegistration(static resolver => + resolver.RegisterLazySingleton>(static () => new TView())); /// /// Builds the application and returns the ReactiveUI instance wrapper. @@ -702,7 +773,16 @@ public IReactiveUIBuilder RegisterSingletonView() throw new InvalidOperationException("Failed to create ReactiveUIInstance instance"); } - InitializeReactiveUI(); + InitializeStaticState(); + + RxConverters.SetService(ConverterService); + + if (_messageBus is not null) + { + MessageBus.Current = _messageBus; + } + + RxAppBuilder.MarkAsInitialized(); return appInstance; } @@ -797,7 +877,11 @@ public IReactiveUIInstance WithInstance(Action(), current.GetService(), current.GetService(), current.GetService()); + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -823,7 +907,12 @@ public IReactiveUIInstance WithInstance(Action(), current.GetService(), current.GetService(), current.GetService(), current.GetService()); + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -851,12 +940,12 @@ public IReactiveUIInstance WithInstance(Action(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -874,7 +963,8 @@ public IReactiveUIInstance WithInstance(ActionThe seventh type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -885,13 +975,13 @@ public IReactiveUIInstance WithInstance(Action(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -910,7 +1000,8 @@ public IReactiveUIInstance WithInstance(ActionThe eighth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -921,14 +1012,14 @@ public IReactiveUIInstance WithInstance(Action(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -948,7 +1039,8 @@ public IReactiveUIInstance WithInstance(ActionThe ninth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -959,15 +1051,15 @@ public IReactiveUIInstance WithInstance(Acti { var current = Current; action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -988,7 +1080,8 @@ public IReactiveUIInstance WithInstance(Acti /// The tenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -999,16 +1092,16 @@ public IReactiveUIInstance WithInstance { var current = Current; action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -1030,7 +1123,8 @@ public IReactiveUIInstance WithInstance /// The eleventh type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -1041,17 +1135,17 @@ public IReactiveUIInstance WithInstance(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -1074,7 +1168,8 @@ public IReactiveUIInstance WithInstanceThe twelfth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -1085,18 +1180,18 @@ public IReactiveUIInstance WithInstance(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -1120,7 +1215,8 @@ public IReactiveUIInstance WithInstanceThe thirteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -1131,19 +1227,19 @@ public IReactiveUIInstance WithInstance(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -1168,7 +1264,8 @@ public IReactiveUIInstance WithInstanceThe fourteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance( + Action action) { if (Current is null) { @@ -1179,20 +1276,20 @@ public IReactiveUIInstance WithInstance(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -1218,7 +1315,23 @@ public IReactiveUIInstance WithInstanceThe fifteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance< + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + T14, + T15>( + Action action) { if (Current is null) { @@ -1229,21 +1342,21 @@ public IReactiveUIInstance WithInstance(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); } return this; @@ -1270,7 +1383,23 @@ public IReactiveUIInstance WithInstanceThe sixteenth type to resolve. /// The action. /// IReactiveUIInstance instance for chaining. - public IReactiveUIInstance WithInstance(Action action) + public IReactiveUIInstance WithInstance< + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11, + T12, + T13, + T14, + T15, + T16>(Action action) { if (Current is null) { @@ -1306,49 +1435,13 @@ public IReactiveUIInstance WithInstance /// The default small cache limit for the current platform. - private static int GetPlatformDefaultSmallCacheLimit() => -#if ANDROID || IOS - 32; -#else - 64; -#endif + private static int GetPlatformDefaultSmallCacheLimit() => DefaultSmallCacheLimit; /// /// Gets the platform-specific default big cache limit. /// /// The default big cache limit for the current platform. - private static int GetPlatformDefaultBigCacheLimit() => -#if ANDROID || IOS - 64; -#else - 256; -#endif - - /// - /// Initializes ReactiveUI global state after the Splat build has completed. - /// - private void InitializeReactiveUI() - { - if (_reactiveUiInitialized) - { - return; - } - - // Initialize static state (cache sizes, exception handler, suspension host) - InitializeStaticState(); - - // Set the global converter service - RxConverters.SetService(ConverterService); - - if (_messageBus is not null) - { - MessageBus.Current = _messageBus; - } - - // Mark ReactiveUI as initialized via builder pattern - RxAppBuilder.MarkAsInitialized(); - _reactiveUiInitialized = true; - } + private static int GetPlatformDefaultBigCacheLimit() => DefaultBigCacheLimit; /// /// Registers all standard ReactiveUI converters to the ConverterService. @@ -1356,11 +1449,9 @@ private void InitializeReactiveUI() /// private void RegisterStandardConverters() { - // General converters ConverterService.TypedConverters.Register(new EqualityTypeConverter()); ConverterService.TypedConverters.Register(new StringConverter()); - // Numeric → String converters ConverterService.TypedConverters.Register(new ByteToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableByteToStringTypeConverter()); ConverterService.TypedConverters.Register(new ShortToStringTypeConverter()); @@ -1376,7 +1467,6 @@ private void RegisterStandardConverters() ConverterService.TypedConverters.Register(new DecimalToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableDecimalToStringTypeConverter()); - // String → Numeric converters ConverterService.TypedConverters.Register(new StringToByteTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableByteTypeConverter()); ConverterService.TypedConverters.Register(new StringToShortTypeConverter()); @@ -1392,55 +1482,46 @@ private void RegisterStandardConverters() ConverterService.TypedConverters.Register(new StringToDecimalTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableDecimalTypeConverter()); - // Boolean ↔ String converters ConverterService.TypedConverters.Register(new BooleanToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableBooleanToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToBooleanTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableBooleanTypeConverter()); - // Guid ↔ String converters ConverterService.TypedConverters.Register(new GuidToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableGuidToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToGuidTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableGuidTypeConverter()); - // DateTime ↔ String converters ConverterService.TypedConverters.Register(new DateTimeToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableDateTimeToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToDateTimeTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableDateTimeTypeConverter()); - // DateTimeOffset ↔ String converters ConverterService.TypedConverters.Register(new DateTimeOffsetToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableDateTimeOffsetToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToDateTimeOffsetTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableDateTimeOffsetTypeConverter()); - // TimeSpan ↔ String converters ConverterService.TypedConverters.Register(new TimeSpanToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableTimeSpanToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToTimeSpanTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableTimeSpanTypeConverter()); #if NET6_0_OR_GREATER - // DateOnly ↔ String converters (.NET 6+) ConverterService.TypedConverters.Register(new DateOnlyToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableDateOnlyToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToDateOnlyTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableDateOnlyTypeConverter()); - // TimeOnly ↔ String converters (.NET 6+) ConverterService.TypedConverters.Register(new TimeOnlyToStringTypeConverter()); ConverterService.TypedConverters.Register(new NullableTimeOnlyToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToTimeOnlyTypeConverter()); ConverterService.TypedConverters.Register(new StringToNullableTimeOnlyTypeConverter()); #endif - // Uri ↔ String converters ConverterService.TypedConverters.Register(new UriToStringTypeConverter()); ConverterService.TypedConverters.Register(new StringToUriTypeConverter()); - // Nullable ↔ Non-Nullable converters ConverterService.TypedConverters.Register(new ByteToNullableByteTypeConverter()); ConverterService.TypedConverters.Register(new NullableByteToByteTypeConverter()); ConverterService.TypedConverters.Register(new ShortToNullableShortTypeConverter()); @@ -1463,35 +1544,37 @@ private void RegisterStandardConverters() /// private void InitializeStaticState() { - // Initialize cache sizes - use configured values or platform defaults var smallCache = _smallCacheLimit ?? GetPlatformDefaultSmallCacheLimit(); var bigCache = _bigCacheLimit ?? GetPlatformDefaultBigCacheLimit(); RxCacheSize.Initialize(smallCache, bigCache); - // Initialize exception handler if configured if (_exceptionHandler is not null) { RxState.InitializeExceptionHandler(_exceptionHandler); } - // Initialize suspension host if configured - if (_suspensionHost is not null) + if (_suspensionHost is null) { - RxSuspension.InitializeSuspensionHost(_suspensionHost); + return; } + + RxSuspension.InitializeSuspensionHost(_suspensionHost); } + /// Registers a deferred action that applies configured schedulers to RxApp during build. private void ConfigureSchedulers() => - WithCustomRegistration(_ => + WithCustomRegistration(_ => + { + if (MainThreadScheduler != null && _setRxAppMainScheduler) { - if (MainThreadScheduler != null && _setRxAppMainScheduler) - { - RxSchedulers.MainThreadScheduler = MainThreadScheduler; - } - - if (TaskpoolScheduler != null && _setRxAppTaskPoolScheduler) - { - RxSchedulers.TaskpoolScheduler = TaskpoolScheduler; - } - }); + RxSchedulers.MainThreadScheduler = MainThreadScheduler; + } + + if (TaskpoolScheduler == null || !_setRxAppTaskPoolScheduler) + { + return; + } + + RxSchedulers.TaskpoolScheduler = TaskpoolScheduler; + }); } diff --git a/src/ReactiveUI/Builder/RxAppBuilder.cs b/src/ReactiveUI/Builder/RxAppBuilder.cs index 8592ad85b1..543153cb1c 100644 --- a/src/ReactiveUI/Builder/RxAppBuilder.cs +++ b/src/ReactiveUI/Builder/RxAppBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,12 +15,15 @@ namespace ReactiveUI.Builder; public static class RxAppBuilder { #if NET9_0_OR_GREATER - private static readonly System.Threading.Lock _resetLock = new(); + /// Synchronizes access to initialization state across threads. + private static readonly Lock _resetLock = new(); #else + /// Synchronizes access to initialization state across threads. private static readonly object _resetLock = new(); #endif - private static int _hasBeenInitialized; // 0 = false, 1 = true + /// Initialization flag: 0 means not initialized, 1 means initialized. + private static int _hasBeenInitialized; /// /// Creates a ReactiveUI builder with the Splat Locator instance. @@ -95,24 +98,14 @@ internal static void ResetForTesting() { lock (_resetLock) { - // Reset schedulers back to defaults RxSchedulers.ResetForTesting(); - // Reset Splat builder state first Splat.Builder.AppBuilder.ResetBuilderStateForTests(); - // Reset the locator to a clean ModernDependencyResolver. - // Using ModernDependencyResolver (which implements IMutableDependencyResolver) ensures - // that AppLocator.CurrentMutable returns a valid, non-disposed mutable resolver after reset. - // InstanceGenericFirstDependencyResolver is read-only and does not implement - // IMutableDependencyResolver, which causes AppLocator.CurrentMutable to still return - // the old (now disposed) ModernDependencyResolver after SetLocator is called with it. AppLocator.SetLocator(new ModernDependencyResolver()); - // Clear activation fetcher cache since it queries the AppLocator ViewForMixins.ResetActivationFetcherCacheForTesting(); - // Finally, reset the initialization flag _hasBeenInitialized = 0; } } diff --git a/src/ReactiveUI/ChangeSets/ChangeSetExtensions.cs b/src/ReactiveUI/ChangeSets/ChangeSetExtensions.cs new file mode 100644 index 0000000000..e2c7cc3953 --- /dev/null +++ b/src/ReactiveUI/ChangeSets/ChangeSetExtensions.cs @@ -0,0 +1,308 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections; +using System.Collections.ObjectModel; +using System.Collections.Specialized; + +namespace ReactiveUI; + +/// +/// Extensions that turn observable collections into a stream of batches, replacing +/// the DynamicData change-set surface used inside ReactiveUI with a tailored, allocation-light layer. +/// +public static class ChangeSetExtensions +{ + /// + /// Observes an as a change-set stream, emitting an initial batch for the + /// current items and then one batch per collection change. + /// + /// The collection item type. + /// The collection to observe. + /// A change-set stream. + public static IObservable> ToObservableChangeSet(this ObservableCollection collection) + { + ArgumentExceptionHelper.ThrowIfNull(collection); + return new ChangeSetObservable, T>(collection); + } + + /// + /// Observes a collection that raises as a change-set stream, emitting an + /// initial batch for the current items and then one batch per collection change. + /// + /// The collection type. + /// The collection item type. + /// The collection to observe. + /// A change-set stream. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "T is the element type and is supplied explicitly by callers; it cannot be inferred from the collection parameter.")] + public static IObservable> ToObservableChangeSet(this TCollection collection) + where TCollection : INotifyCollectionChanged, IEnumerable + { + ArgumentExceptionHelper.ThrowIfNull(collection); + return new ChangeSetObservable(collection); + } + + /// A change set backed by a list of changes, exposing the add/remove counts. + /// The collection item type. + internal sealed class ReactiveChangeSet : IReactiveChangeSet + { + /// The changes in this batch. + private readonly List> _changes; + + /// Initializes a new instance of the class. + /// The changes in this batch. + public ReactiveChangeSet(List> changes) + { + _changes = changes; + for (var i = 0; i < changes.Count; i++) + { + var reason = changes[i].Reason; + if (reason == ReactiveChangeReason.Add) + { + Adds++; + } + else if (reason == ReactiveChangeReason.Remove) + { + Removes++; + } + } + } + + /// + public int Count => _changes.Count; + + /// + public int Adds { get; } + + /// + public int Removes { get; } + + /// + public ReactiveChange this[int index] => _changes[index]; + + /// Returns an enumerator over the changes. + /// An enumerator over the changes. + public List>.Enumerator GetEnumerator() => _changes.GetEnumerator(); + + /// + IEnumerator> IEnumerable>.GetEnumerator() => _changes.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => _changes.GetEnumerator(); + } + + /// + /// Observes a notifying collection and translates each into a + /// flattened per-item change set, maintaining a shadow copy to service the initial batch and reset translation. + /// + /// The collection type. + /// The collection item type. + /// The collection to observe. + private sealed class ChangeSetObservable(TCollection collection) : IObservable> + where TCollection : INotifyCollectionChanged, IEnumerable + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var subscription = new Subscription(collection, observer); + subscription.Start(); + return subscription; + } + + /// Hooks the collection, emits the initial batch, and translates subsequent changes. + /// The collection to observe. + /// The observer receiving change sets. + private sealed class Subscription(TCollection collection, IObserver> observer) : IDisposable + { + /// Shadow copy of the collection, kept in sync to service resets and indices. + private readonly List _shadow = []; + + /// Emits the initial batch for the current items and begins observing changes. + public void Start() + { + _shadow.AddRange(collection); + + if (_shadow.Count > 0) + { + var initial = new List>(_shadow.Count); + for (var i = 0; i < _shadow.Count; i++) + { + initial.Add(new ReactiveChange(ReactiveChangeReason.Add, _shadow[i], default, i, -1)); + } + + observer.OnNext(new ReactiveChangeSet(initial)); + } + + collection.CollectionChanged += OnCollectionChanged; + } + + /// + public void Dispose() => collection.CollectionChanged -= OnCollectionChanged; + + /// Translates a collection-changed event into a change set and forwards it. + /// The collection. + /// The collection-changed event arguments. + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + var changes = new List>(); + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + { + ApplyAdd(e, changes); + break; + } + + case NotifyCollectionChangedAction.Remove: + { + ApplyRemove(e, changes); + break; + } + + case NotifyCollectionChangedAction.Replace: + { + ApplyReplace(e, changes); + break; + } + + case NotifyCollectionChangedAction.Move: + { + ApplyMove(e, changes); + break; + } + + case NotifyCollectionChangedAction.Reset: + { + ApplyReset(changes); + break; + } + } + + if (changes.Count == 0) + { + return; + } + + observer.OnNext(new ReactiveChangeSet(changes)); + } + + /// Records added items into the shadow and the change list. + /// The collection-changed event arguments. + /// The change list being built. + private void ApplyAdd(NotifyCollectionChangedEventArgs e, List> changes) + { + if (e.NewItems is null) + { + return; + } + + var start = e.NewStartingIndex; + for (var i = 0; i < e.NewItems.Count; i++) + { + var item = (T)e.NewItems[i]!; + var index = start < 0 ? _shadow.Count : start + i; + _shadow.Insert(index, item); + changes.Add(new ReactiveChange(ReactiveChangeReason.Add, item, default, index, -1)); + } + } + + /// Records removed items out of the shadow and into the change list. + /// The collection-changed event arguments. + /// The change list being built. + private void ApplyRemove(NotifyCollectionChangedEventArgs e, List> changes) + { + if (e.OldItems is null) + { + return; + } + + var start = e.OldStartingIndex; + for (var i = 0; i < e.OldItems.Count; i++) + { + var item = (T)e.OldItems[i]!; + var index = start < 0 ? -1 : start; + RemoveFromShadow(index, item); + changes.Add(new ReactiveChange(ReactiveChangeReason.Remove, item, default, index, -1)); + } + } + + /// Records replaced items in the shadow and the change list. + /// The collection-changed event arguments. + /// The change list being built. + private void ApplyReplace(NotifyCollectionChangedEventArgs e, List> changes) + { + if (e.NewItems is null || e.OldItems is null) + { + return; + } + + var start = e.NewStartingIndex; + for (var i = 0; i < e.NewItems.Count; i++) + { + var current = (T)e.NewItems[i]!; + var previous = (T)e.OldItems[i]!; + var index = start < 0 ? -1 : start + i; + if (index >= 0 && index < _shadow.Count) + { + _shadow[index] = current; + } + + changes.Add(new ReactiveChange(ReactiveChangeReason.Replace, current, previous, index, -1)); + } + } + + /// Records a moved item in the shadow and the change list. + /// The collection-changed event arguments. + /// The change list being built. + private void ApplyMove(NotifyCollectionChangedEventArgs e, List> changes) + { + if (e.NewItems is null) + { + return; + } + + var item = (T)e.NewItems[0]!; + if (e.OldStartingIndex >= 0 && e.OldStartingIndex < _shadow.Count) + { + _shadow.RemoveAt(e.OldStartingIndex); + } + + var index = e.NewStartingIndex < 0 ? _shadow.Count : e.NewStartingIndex; + _shadow.Insert(index, item); + changes.Add(new ReactiveChange(ReactiveChangeReason.Move, item, default, index, e.OldStartingIndex)); + } + + /// Translates a reset into one remove per prior item and clears the shadow. + /// The change list being built. + private void ApplyReset(List> changes) + { + for (var i = 0; i < _shadow.Count; i++) + { + changes.Add(new ReactiveChange(ReactiveChangeReason.Remove, _shadow[i], default, i, -1)); + } + + _shadow.Clear(); + } + + /// Removes an item from the shadow by index when valid, otherwise by value. + /// The index to remove at, or -1 to remove by value. + /// The item to remove by value when the index is unusable. + private void RemoveFromShadow(int index, T item) + { + if (index >= 0 && index < _shadow.Count) + { + _shadow.RemoveAt(index); + return; + } + + _shadow.Remove(item); + } + } + } +} diff --git a/src/ReactiveUI/ChangeSets/CollectionChanged.cs b/src/ReactiveUI/ChangeSets/CollectionChanged.cs new file mode 100644 index 0000000000..79408f35f5 --- /dev/null +++ b/src/ReactiveUI/ChangeSets/CollectionChanged.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Specialized; + +namespace ReactiveUI; + +/// +/// A single notification, carrying the sender and the event +/// arguments. Replaces the EventPattern<NotifyCollectionChangedEventArgs> element emitted by +/// DynamicData's ObserveCollectionChanges. +/// +public readonly struct CollectionChanged : IEquatable +{ + /// Initializes a new instance of the struct. + /// The collection that raised the event. + /// The collection-changed event arguments. + public CollectionChanged(object? sender, NotifyCollectionChangedEventArgs eventArgs) + { + Sender = sender; + EventArgs = eventArgs; + } + + /// Gets the collection that raised the event. + public object? Sender { get; } + + /// Gets the collection-changed event arguments. + public NotifyCollectionChangedEventArgs EventArgs { get; } + + /// Determines whether two notifications are equal. + /// The first notification. + /// The second notification. + /// if equal. + public static bool operator ==(CollectionChanged left, CollectionChanged right) => left.Equals(right); + + /// Determines whether two notifications are unequal. + /// The first notification. + /// The second notification. + /// if unequal. + public static bool operator !=(CollectionChanged left, CollectionChanged right) => !left.Equals(right); + + /// + public bool Equals(CollectionChanged other) => + ReferenceEquals(Sender, other.Sender) && ReferenceEquals(EventArgs, other.EventArgs); + + /// + public override bool Equals(object? obj) => obj is CollectionChanged other && Equals(other); + + /// + public override int GetHashCode() => HashCode.Combine(Sender, EventArgs); +} diff --git a/src/ReactiveUI/ChangeSets/CollectionChangedExtensions.cs b/src/ReactiveUI/ChangeSets/CollectionChangedExtensions.cs new file mode 100644 index 0000000000..3c95c28067 --- /dev/null +++ b/src/ReactiveUI/ChangeSets/CollectionChangedExtensions.cs @@ -0,0 +1,68 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Specialized; + +namespace ReactiveUI; + +/// +/// Extensions that expose as an observable, replacing +/// DynamicData's ObserveCollectionChanges with a single tailored event-bridge layer. +/// +public static class CollectionChangedExtensions +{ + /// + /// Observes a collection's event as a stream of + /// notifications. + /// + /// The collection to observe. + /// A stream of collection-changed notifications. + public static IObservable ObserveCollectionChanges(this INotifyCollectionChanged source) + { + ArgumentExceptionHelper.ThrowIfNull(source); + return new CollectionChangedObservable(source); + } + + /// Forwards each collection-changed event to subscribers for the lifetime of the subscription. + /// The collection to observe. + private sealed class CollectionChangedObservable(INotifyCollectionChanged source) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Subscription(source, observer); + } + + /// Attaches the collection-changed handler and detaches it on dispose. + private sealed class Subscription : IDisposable + { + /// The observed collection. + private readonly INotifyCollectionChanged _source; + + /// The observer receiving notifications. + private readonly IObserver _observer; + + /// Initializes a new instance of the class and hooks the event. + /// The collection to observe. + /// The observer receiving notifications. + public Subscription(INotifyCollectionChanged source, IObserver observer) + { + _source = source; + _observer = observer; + _source.CollectionChanged += OnCollectionChanged; + } + + /// + public void Dispose() => _source.CollectionChanged -= OnCollectionChanged; + + /// Forwards a collection-changed event to the observer. + /// The collection that raised the event. + /// The collection-changed event arguments. + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) => + _observer.OnNext(new CollectionChanged(sender, e)); + } + } +} diff --git a/src/ReactiveUI/ChangeSets/IReactiveChangeSet.cs b/src/ReactiveUI/ChangeSets/IReactiveChangeSet.cs new file mode 100644 index 0000000000..2f433f51f8 --- /dev/null +++ b/src/ReactiveUI/ChangeSets/IReactiveChangeSet.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections; + +namespace ReactiveUI; + +/// +/// A non-generic view of a batch of collection changes, exposing the add/remove counts used to detect count changes. +/// +public interface IReactiveChangeSet : IEnumerable +{ + /// Gets the number of changes in the set. + int Adds { get; } + + /// Gets the number of changes in the set. + int Removes { get; } +} diff --git a/src/ReactiveUI/ChangeSets/IReactiveChangeSet{T}.cs b/src/ReactiveUI/ChangeSets/IReactiveChangeSet{T}.cs new file mode 100644 index 0000000000..f14fb2598f --- /dev/null +++ b/src/ReactiveUI/ChangeSets/IReactiveChangeSet{T}.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI; + +/// +/// A batch of collection changes produced by the change-set observers. +/// +/// The collection item type. +public interface IReactiveChangeSet : IReactiveChangeSet, IReadOnlyList>; diff --git a/src/ReactiveUI/ChangeSets/ReactiveChange.cs b/src/ReactiveUI/ChangeSets/ReactiveChange.cs new file mode 100644 index 0000000000..b2a9dc7608 --- /dev/null +++ b/src/ReactiveUI/ChangeSets/ReactiveChange.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI; + +/// +/// Describes a single change to a collection: the reason, the affected item, and (where relevant) the previous item +/// and indices. +/// +/// The collection item type. +public readonly struct ReactiveChange : IEquatable> +{ + /// Initializes a new instance of the struct. + /// The reason for the change. + /// The current (added/replacing/moved/refreshed) item. + /// The previous item for a ; otherwise default. + /// The item's current index, or -1 when unknown. + /// The item's previous index for a , or -1. + public ReactiveChange(ReactiveChangeReason reason, T current, T? previous, int currentIndex, int previousIndex) + { + Reason = reason; + Current = current; + Previous = previous; + CurrentIndex = currentIndex; + PreviousIndex = previousIndex; + } + + /// Gets the reason for the change. + public ReactiveChangeReason Reason { get; } + + /// Gets the current item (added, replacing, moved, or refreshed). + public T Current { get; } + + /// Gets the previous item for a ; otherwise default. + public T? Previous { get; } + + /// Gets the item's current index, or -1 when unknown. + public int CurrentIndex { get; } + + /// Gets the item's previous index for a , or -1. + public int PreviousIndex { get; } + + /// Determines whether two changes are equal. + /// The first change. + /// The second change. + /// if equal. + public static bool operator ==(ReactiveChange left, ReactiveChange right) => left.Equals(right); + + /// Determines whether two changes are unequal. + /// The first change. + /// The second change. + /// if unequal. + public static bool operator !=(ReactiveChange left, ReactiveChange right) => !left.Equals(right); + + /// + public bool Equals(ReactiveChange other) => + Reason == other.Reason && + EqualityComparer.Default.Equals(Current, other.Current) && + EqualityComparer.Default.Equals(Previous, other.Previous) && + CurrentIndex == other.CurrentIndex && + PreviousIndex == other.PreviousIndex; + + /// + public override bool Equals(object? obj) => obj is ReactiveChange other && Equals(other); + + /// + public override int GetHashCode() => HashCode.Combine(Reason, Current, Previous, CurrentIndex, PreviousIndex); +} diff --git a/src/ReactiveUI/ChangeSets/ReactiveChangeReason.cs b/src/ReactiveUI/ChangeSets/ReactiveChangeReason.cs new file mode 100644 index 0000000000..9568acc7b5 --- /dev/null +++ b/src/ReactiveUI/ChangeSets/ReactiveChangeReason.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI; + +/// +/// The reason a single occurred. Range and reset operations are flattened to one +/// change per affected item, so this is a per-item reason rather than a batch reason. +/// +public enum ReactiveChangeReason +{ + /// An item was added to the collection. + Add, + + /// An item was removed from the collection (a reset is reported as one remove per prior item). + Remove, + + /// An item replaced an existing item at the same index. + Replace, + + /// An item moved from one index to another (count is unchanged). + Move, + + /// An item signalled that it should be re-evaluated without being added or removed. + Refresh, +} diff --git a/src/ReactiveUI/Comparers/ChainedComparer.cs b/src/ReactiveUI/Comparers/ChainedComparer.cs index d057164a14..5ac0e8bfad 100644 --- a/src/ReactiveUI/Comparers/ChainedComparer.cs +++ b/src/ReactiveUI/Comparers/ChainedComparer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Comparers/ComparerChainingExtensions.cs b/src/ReactiveUI/Comparers/ComparerChainingExtensions.cs index 86b65bc467..5a11f34a7f 100644 --- a/src/ReactiveUI/Comparers/ComparerChainingExtensions.cs +++ b/src/ReactiveUI/Comparers/ComparerChainingExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -25,7 +25,8 @@ public static class ComparerChainingExtensions /// A function supplying the values for the comparer. /// /// A comparer. - public static IComparer ThenBy(this IComparer? parent, Func selector) => ThenBy(parent, selector, Comparer.Default); + public static IComparer ThenBy(this IComparer? parent, Func selector) => + ThenBy(parent, selector, Comparer.Default); /// /// Creates a derived comparer based on the given parent comparer. The returned comparer will sort elements @@ -46,7 +47,11 @@ public static class ComparerChainingExtensions /// The comparer to use when comparing the values returned by the selector. /// /// A comparer. - public static IComparer ThenBy(this IComparer? parent, Func selector, IComparer comparer) => new ChainedComparer(parent, (x, y) => comparer.Compare(selector(x), selector(y))); + public static IComparer ThenBy( + this IComparer? parent, + Func selector, + IComparer comparer) => + new ChainedComparer(parent, (x, y) => comparer.Compare(selector(x), selector(y))); /// /// Creates a derived comparer based on the given parent comparer. The returned comparer will sort elements @@ -63,7 +68,8 @@ public static class ComparerChainingExtensions /// A function supplying the values for the comparer. /// /// A comparer. - public static IComparer ThenByDescending(this IComparer? parent, Func selector) => ThenByDescending(parent, selector, Comparer.Default); + public static IComparer ThenByDescending(this IComparer? parent, Func selector) => + ThenByDescending(parent, selector, Comparer.Default); /// /// Creates a derived comparer based on the given parent comparer. The returned comparer will sort elements @@ -84,5 +90,9 @@ public static class ComparerChainingExtensions /// The comparer to use when comparing the values returned by the selector. /// /// A comparer. - public static IComparer ThenByDescending(this IComparer? parent, Func selector, IComparer comparer) => new ChainedComparer(parent, (x, y) => -comparer.Compare(selector(x), selector(y))); + public static IComparer ThenByDescending( + this IComparer? parent, + Func selector, + IComparer comparer) => + new ChainedComparer(parent, (x, y) => -comparer.Compare(selector(x), selector(y))); } diff --git a/src/ReactiveUI/Comparers/IComparerBuilder.cs b/src/ReactiveUI/Comparers/IComparerBuilder.cs index 093841d665..7260eed7be 100644 --- a/src/ReactiveUI/Comparers/IComparerBuilder.cs +++ b/src/ReactiveUI/Comparers/IComparerBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Comparers/OrderedComparer.cs b/src/ReactiveUI/Comparers/OrderedComparer.cs index 4466051446..f78ed27e5b 100644 --- a/src/ReactiveUI/Comparers/OrderedComparer.cs +++ b/src/ReactiveUI/Comparers/OrderedComparer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -32,6 +32,10 @@ public static class OrderedComparer /// /// The comparison type. /// A comparer builder. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IComparerBuilder For() => OrderedComparerTypeWrapper.Instance; /// @@ -44,6 +48,9 @@ public static class OrderedComparer /// The type of elements to compare. private sealed class OrderedComparerTypeWrapper : IComparerBuilder { + /// + /// The singleton instance of this wrapper used to provide the IComparerBuilder for type T. + /// public static readonly OrderedComparerTypeWrapper Instance = new(); /// @@ -67,7 +74,8 @@ private sealed class OrderedComparerTypeWrapper : IComparerBuilder /// A function that extracts the key from an element to use for ordering. Cannot be null. /// An object that compares keys for ordering. If null, the default comparer for the key type is used. /// An IComparer{T} that compares elements based on the specified key and comparer. - public IComparer OrderBy(Func selector, IComparer comparer) => OrderedComparer.OrderBy(selector, comparer); + public IComparer OrderBy(Func selector, IComparer comparer) => + OrderedComparer.OrderBy(selector, comparer); /// /// Creates a comparer that orders elements in descending order according to a specified key selector. @@ -77,7 +85,8 @@ private sealed class OrderedComparerTypeWrapper : IComparerBuilder /// The type of the key returned by the selector function. /// A function to extract the key from an element for comparison. Cannot be null. /// An IComparer{T} that compares elements based on the descending order of the selected key. - public IComparer OrderByDescending(Func selector) => OrderedComparer.OrderByDescending(selector); + public IComparer OrderByDescending(Func selector) => + OrderedComparer.OrderByDescending(selector); /// /// Creates a comparer that orders elements in descending order according to a specified key selector and @@ -87,68 +96,7 @@ private sealed class OrderedComparerTypeWrapper : IComparerBuilder /// A function that extracts the key from an element to determine its order. Cannot be null. /// An optional comparer to use for comparing keys. If null, the default comparer for the key type is used. /// An IComparer{T} that compares elements in descending order based on the specified key and comparer. - public IComparer OrderByDescending(Func selector, IComparer comparer) => OrderedComparer.OrderByDescending(selector, comparer); + public IComparer OrderByDescending(Func selector, IComparer comparer) => + OrderedComparer.OrderByDescending(selector, comparer); } } - -/// -/// Convenience class providing a starting point for chaining comparers. -/// -/// The comparison type. -[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Classes with the same class names within.")] -public static class OrderedComparer -{ - /// - /// Creates a comparer that will sort elements in ascending order based on the values returned by the provided - /// selector. The values will be compared using the default comparer for the return type of the selector. - /// - /// The value type. - /// - /// A function supplying the values for the comparer. - /// - /// A comparer. - public static IComparer OrderBy(Func selector) => ComparerChainingExtensions.ThenBy(null, selector); - - /// - /// Creates a comparer that will sort elements in ascending order based on the values returned by the provided - /// selector. The selector values will be compared using the provided comparer or the default comparer for the - /// return type of the selector if no comparer is specified. - /// - /// The value type. - /// - /// A function supplying the values for the comparer. - /// - /// - /// The comparer to use when comparing the values returned by the selector. - /// The default comparer for that type will be used if this parameter is null. - /// - /// A comparer. - public static IComparer OrderBy(Func selector, IComparer comparer) => ComparerChainingExtensions.ThenBy(null, selector, comparer); - - /// - /// Creates a comparer that will sort elements in descending order based on the values returned by the provided - /// selector. The values will be compared using the default comparer for the return type of the selector. - /// - /// The value type. - /// - /// A function supplying the values for the comparer. - /// - /// A comparer. - public static IComparer OrderByDescending(Func selector) => ComparerChainingExtensions.ThenByDescending(null, selector); - - /// - /// Creates a comparer that will sort elements in descending order based on the values returned by the provided - /// selector. The selector values will be compared using the provided comparer or the default comparer for the - /// return type of the selector if no comparer is specified. - /// - /// The value type. - /// - /// A function supplying the values for the comparer. - /// - /// - /// The comparer to use when comparing the values returned by the selector. - /// The default comparer for that type will be used if this parameter is null. - /// - /// A comparer. - public static IComparer OrderByDescending(Func selector, IComparer comparer) => ComparerChainingExtensions.ThenByDescending(null, selector, comparer); -} diff --git a/src/ReactiveUI/Comparers/OrderedComparer{T}.cs b/src/ReactiveUI/Comparers/OrderedComparer{T}.cs new file mode 100644 index 0000000000..154839ae85 --- /dev/null +++ b/src/ReactiveUI/Comparers/OrderedComparer{T}.cs @@ -0,0 +1,71 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI; + +/// +/// Convenience class providing a starting point for chaining comparers. +/// +/// The comparison type. +public static class OrderedComparer +{ + /// + /// Creates a comparer that will sort elements in ascending order based on the values returned by the provided + /// selector. The values will be compared using the default comparer for the return type of the selector. + /// + /// The value type. + /// + /// A function supplying the values for the comparer. + /// + /// A comparer. + public static IComparer OrderBy(Func selector) => + ComparerChainingExtensions.ThenBy(null, selector); + + /// + /// Creates a comparer that will sort elements in ascending order based on the values returned by the provided + /// selector. The selector values will be compared using the provided comparer or the default comparer for the + /// return type of the selector if no comparer is specified. + /// + /// The value type. + /// + /// A function supplying the values for the comparer. + /// + /// + /// The comparer to use when comparing the values returned by the selector. + /// The default comparer for that type will be used if this parameter is null. + /// + /// A comparer. + public static IComparer OrderBy(Func selector, IComparer comparer) => + ComparerChainingExtensions.ThenBy(null, selector, comparer); + + /// + /// Creates a comparer that will sort elements in descending order based on the values returned by the provided + /// selector. The values will be compared using the default comparer for the return type of the selector. + /// + /// The value type. + /// + /// A function supplying the values for the comparer. + /// + /// A comparer. + public static IComparer OrderByDescending(Func selector) => + ComparerChainingExtensions.ThenByDescending(null, selector); + + /// + /// Creates a comparer that will sort elements in descending order based on the values returned by the provided + /// selector. The selector values will be compared using the provided comparer or the default comparer for the + /// return type of the selector if no comparer is specified. + /// + /// The value type. + /// + /// A function supplying the values for the comparer. + /// + /// + /// The comparer to use when comparing the values returned by the selector. + /// The default comparer for that type will be used if this parameter is null. + /// + /// A comparer. + public static IComparer OrderByDescending(Func selector, IComparer comparer) => + ComparerChainingExtensions.ThenByDescending(null, selector, comparer); +} diff --git a/src/ReactiveUI/EventHandlers/LocalizableAttribute.cs b/src/ReactiveUI/EventHandlers/LocalizableAttribute.cs index ba9b323bb7..8704dae713 100644 --- a/src/ReactiveUI/EventHandlers/LocalizableAttribute.cs +++ b/src/ReactiveUI/EventHandlers/LocalizableAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Expression/ExpressionRewriter.cs b/src/ReactiveUI/Expression/ExpressionRewriter.cs index 7b263fffb2..8ccd24bd09 100644 --- a/src/ReactiveUI/Expression/ExpressionRewriter.cs +++ b/src/ReactiveUI/Expression/ExpressionRewriter.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Collections.ObjectModel; +using System.Linq.Expressions; using System.Reflection; using System.Text; @@ -68,10 +70,18 @@ public override Expression Visit(Expression? node) /// The binary expression node to visit. Must represent an array or indexer access with a constant index. /// An that represents the rewritten array or indexer access. /// Thrown if the right side of the binary expression is not a constant expression. - [RequiresUnreferencedCode("Expression rewriting uses reflection over runtime types (e.g., Item/Length) which may be removed by trimming.")] - [RequiresDynamicCode("Expression rewriting uses reflection over runtime types and may not be compatible with AOT compilation.")] - [SuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "Third Party Code")] - [SuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "Third Party Code")] + [RequiresUnreferencedCode( + "Expression rewriting uses reflection over runtime types (e.g., Item/Length) which may be removed by trimming.")] + [RequiresDynamicCode( + "Expression rewriting uses reflection over runtime types and may not be compatible with AOT compilation.")] + [SuppressMessage( + "AOT", + "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "Third Party Code")] + [SuppressMessage( + "Trimming", + "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "Third Party Code")] protected override Expression VisitBinary(BinaryExpression node) { if (node.Right is not ConstantExpression) @@ -87,7 +97,6 @@ protected override Expression VisitBinary(BinaryExpression node) return Expression.ArrayAccess(instance, index); } - // Translate arrayindex into a normal index expression using the indexer property. return Expression.MakeIndex(instance, GetItemProperty(instance.Type), [index]); } @@ -101,10 +110,18 @@ protected override Expression VisitBinary(BinaryExpression node) /// An representing the rewritten unary expression, or the original node if no rewriting is /// required. /// Thrown if does not have a valid operand. - [RequiresUnreferencedCode("Expression rewriting uses reflection over runtime types (e.g., Item/Length) which may be removed by trimming.")] - [RequiresDynamicCode("Expression rewriting uses reflection over runtime types and may not be compatible with AOT compilation.")] - [SuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "Third Party Code")] - [SuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "Third Party Code")] + [RequiresUnreferencedCode( + "Expression rewriting uses reflection over runtime types (e.g., Item/Length) which may be removed by trimming.")] + [RequiresDynamicCode( + "Expression rewriting uses reflection over runtime types and may not be compatible with AOT compilation.")] + [SuppressMessage( + "AOT", + "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "Third Party Code")] + [SuppressMessage( + "Trimming", + "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "Third Party Code")] protected override Expression VisitUnary(UnaryExpression node) { if (node.Operand is null) @@ -112,21 +129,21 @@ protected override Expression VisitUnary(UnaryExpression node) throw new ArgumentException("Could not find a valid operand for the node.", nameof(node)); } - if (node.NodeType == ExpressionType.Convert) + switch (node.NodeType) { - // Strip conversion nodes to keep expression chains stable. - return Visit(node.Operand); - } + case ExpressionType.Convert: + return Visit(node.Operand); + case ExpressionType.ArrayLength: + { + var operand = Visit(node.Operand); + var lengthProperty = GetLengthProperty(operand.Type); - if (node.NodeType == ExpressionType.ArrayLength) - { - var operand = Visit(node.Operand); - var lengthProperty = GetLengthProperty(operand.Type); + return Expression.MakeMemberAccess(operand, lengthProperty); + } - return Expression.MakeMemberAccess(operand, lengthProperty); + default: + return node.Update(Visit(node.Operand)); } - - return node.Update(Visit(node.Operand)); } /// @@ -142,10 +159,18 @@ protected override Expression VisitUnary(UnaryExpression node) /// An expression representing the rewritten indexer access. /// Thrown if the method call does not represent an indexer access with all constant arguments. /// Thrown if the method call does not target a valid object instance. - [RequiresUnreferencedCode("Expression rewriting uses reflection over runtime types (e.g., Item/Length) which may be removed by trimming.")] - [RequiresDynamicCode("Expression rewriting uses reflection over runtime types and may not be compatible with AOT compilation.")] - [SuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "Third Party Code")] - [SuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "Third Party Code")] + [RequiresUnreferencedCode( + "Expression rewriting uses reflection over runtime types (e.g., Item/Length) which may be removed by trimming.")] + [RequiresDynamicCode( + "Expression rewriting uses reflection over runtime types and may not be compatible with AOT compilation.")] + [SuppressMessage( + "AOT", + "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "Third Party Code")] + [SuppressMessage( + "Trimming", + "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", + Justification = "Third Party Code")] protected override Expression VisitMethodCall(MethodCallExpression node) { if (!node.Method.IsSpecialName || !AllConstant(node.Arguments)) @@ -160,7 +185,6 @@ protected override Expression VisitMethodCall(MethodCallExpression node) var instance = Visit(node.Object); - // Visit arguments explicitly to avoid LINQ allocations. var args = VisitArgumentList(node.Arguments); return Expression.MakeIndex(instance, GetItemProperty(instance.Type), args); @@ -187,23 +211,22 @@ protected override Expression VisitIndex(IndexExpression node) /// /// The unsupported node. /// An exception to throw. - private static Exception CreateUnsupportedNodeException(Expression node) + private static NotSupportedException CreateUnsupportedNodeException(Expression node) { - // Preserve prior behavior: include helpful guidance for binary expressions. - var sb = new StringBuilder(96); + StringBuilder sb = new(96); sb.Append("Unsupported expression of type '") - .Append(node.NodeType) - .Append("' ") - .Append(node) - .Append('.'); + .Append(node.NodeType) + .Append("' ") + .Append(node) + .Append('.'); if (node is BinaryExpression be) { sb.Append(" Did you meant to use expressions '") - .Append(be.Left) - .Append("' and '") - .Append(be.Right) - .Append("'?"); + .Append(be.Left) + .Append("' and '") + .Append(be.Right) + .Append("'?"); } return new NotSupportedException(sb.ToString()); @@ -216,10 +239,10 @@ private static Exception CreateUnsupportedNodeException(Expression node) /// The resolved indexer property. /// Thrown when no indexer property can be found. private static PropertyInfo GetItemProperty( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { - // NOTE: Using the Type instance preserves trimming annotations; do not reconstruct Type from RuntimeTypeHandle. var property = type.GetRuntimeProperty("Item"); return property ?? throw new InvalidOperationException("Could not find a valid indexer property named 'Item'."); } @@ -231,12 +254,13 @@ private static PropertyInfo GetItemProperty( /// The resolved length property. /// Thrown when no length property can be found. private static PropertyInfo GetLengthProperty( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { - // NOTE: Using the Type instance preserves trimming annotations; do not reconstruct Type from RuntimeTypeHandle. var property = type.GetRuntimeProperty("Length"); - return property ?? throw new InvalidOperationException("Could not find valid information for the array length operator."); + return property ?? + throw new InvalidOperationException("Could not find valid information for the array length operator."); } /// @@ -267,7 +291,7 @@ private Expression[] VisitArgumentList(ReadOnlyCollection arguments) var count = arguments.Count; if (count == 0) { - return Array.Empty(); + return []; } var visited = new Expression[count]; diff --git a/src/ReactiveUI/Expression/Reflection.cs b/src/ReactiveUI/Expression/Reflection.cs index ed88e37f3f..35c4c4481e 100644 --- a/src/ReactiveUI/Expression/Reflection.cs +++ b/src/ReactiveUI/Expression/Reflection.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; @@ -25,6 +26,11 @@ namespace ReactiveUI; [Preserve(AllMembers = true)] public static class Reflection { + /// + /// Error message used when an expression chain is empty. + /// + private const string EmptyExpressionChainMessage = "Expression chain must contain at least one element."; + /// /// Cached expression rewriter used to simplify expression trees. /// @@ -61,14 +67,14 @@ public static class Reflection /// This method intentionally follows existing behavior, including the assumption that index arguments are /// instances. /// - public static string ExpressionToPropertyNames(Expression? expression) // TODO: Create Test + public static string ExpressionToPropertyNames(Expression? expression) { ArgumentExceptionHelper.ThrowIfNull(expression); - var sb = new StringBuilder(); + StringBuilder sb = new(); var firstSegment = true; - foreach (var exp in expression!.GetExpressionChain()) + foreach (var exp in expression.GetExpressionChain()) { if (exp.NodeType == ExpressionType.Parameter) { @@ -80,29 +86,34 @@ public static string ExpressionToPropertyNames(Expression? expression) // TODO: sb.Append('.'); } - if (exp.NodeType == ExpressionType.Index && - exp is IndexExpression indexExpression && - indexExpression.Indexer is not null) + switch (exp.NodeType) { - sb.Append(indexExpression.Indexer.Name).Append('['); - - var args = indexExpression.Arguments; - for (var i = 0; i < args.Count; i++) - { - if (i != 0) + case ExpressionType.Index when + exp is IndexExpression indexExpression && + indexExpression.Indexer is not null: { - sb.Append(','); - } + sb.Append(indexExpression.Indexer.Name).Append('['); - // Preserve original behavior: assumes ConstantExpression. - sb.Append(((ConstantExpression)args[i]).Value); - } + var args = indexExpression.Arguments; + for (var i = 0; i < args.Count; i++) + { + if (i != 0) + { + sb.Append(','); + } - sb.Append(']'); - } - else if (exp.NodeType == ExpressionType.MemberAccess && exp is MemberExpression memberExpression) - { - sb.Append(memberExpression.Member.Name); + sb.Append(((ConstantExpression)args[i]).Value); + } + + sb.Append(']'); + break; + } + + case ExpressionType.MemberAccess when exp is MemberExpression memberExpression: + { + sb.Append(memberExpression.Member.Name); + break; + } } firstSegment = false; @@ -129,13 +140,13 @@ exp is IndexExpression indexExpression && /// Trimming note: this method does not discover members by name; it operates on an already-resolved . /// /// - public static Func? GetValueFetcherForProperty(MemberInfo? member) // TODO: Create Test + public static Func? + GetValueFetcherForProperty(MemberInfo? member) { ArgumentExceptionHelper.ThrowIfNull(member); if (member is FieldInfo field) { - // Delegate captures 'field' (setup-time), avoiding repeated dynamic checks. return (obj, _) => { var value = field.GetValue(obj); @@ -143,12 +154,12 @@ exp is IndexExpression indexExpression && }; } - if (member is PropertyInfo property) + if (member is not PropertyInfo property) { - return property.GetValue; + return null; } - return null; + return property.GetValue; } /// @@ -159,12 +170,13 @@ exp is IndexExpression indexExpression && /// A delegate that takes (target, indexArguments) and returns the value. /// Thrown when is . /// Thrown when is not a field or property. - public static Func GetValueFetcherOrThrow(MemberInfo? member) // TODO: Create Test + public static Func GetValueFetcherOrThrow(MemberInfo? member) { ArgumentExceptionHelper.ThrowIfNull(member); var ret = GetValueFetcherForProperty(member); - return ret ?? throw new ArgumentException($"Type '{member!.DeclaringType}' must have a property '{member.Name}'"); + return ret ?? + throw new ArgumentException($"Type '{member.DeclaringType}' must have a property '{member.Name}'"); } /// @@ -186,7 +198,8 @@ exp is IndexExpression indexExpression && /// Trimming note: this method does not discover members by name; it operates on an already-resolved . /// /// - public static Action? GetValueSetterForProperty(MemberInfo? member) // TODO: Create Test + public static Action? + GetValueSetterForProperty(MemberInfo? member) { ArgumentExceptionHelper.ThrowIfNull(member); @@ -195,12 +208,12 @@ exp is IndexExpression indexExpression && return (obj, val, _) => field.SetValue(obj, val); } - if (member is PropertyInfo property) + if (member is not PropertyInfo property) { - return property.SetValue; + return null; } - return null; + return property.SetValue; } /// @@ -211,12 +224,13 @@ exp is IndexExpression indexExpression && /// A delegate that takes (target, value, indexArguments) and sets the value. /// Thrown when is . /// Thrown when is not a field or property. - public static Action GetValueSetterOrThrow(MemberInfo? member) // TODO: Create Test + public static Action GetValueSetterOrThrow(MemberInfo? member) { ArgumentExceptionHelper.ThrowIfNull(member); var ret = GetValueSetterForProperty(member); - return ret ?? throw new ArgumentException($"Type '{member!.DeclaringType}' must have a property '{member.Name}'"); + return ret ?? + throw new ArgumentException($"Type '{member.DeclaringType}' must have a property '{member.Name}'"); } /// @@ -234,14 +248,17 @@ exp is IndexExpression indexExpression && /// to express a complete trimming contract locally. /// [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static bool TryGetValueForPropertyChain(out TValue changeValue, object? current, IEnumerable expressionChain) // TODO: Create Test + public static bool TryGetValueForPropertyChain( + out TValue changeValue, + object? current, + IEnumerable expressionChain) { var expressions = MaterializeExpressions(expressionChain); var count = expressions.Length; if (count == 0) { - throw new InvalidOperationException("Expression chain must contain at least one element."); + throw new InvalidOperationException(EmptyExpressionChainMessage); } for (var i = 0; i < count - 1; i++) @@ -263,7 +280,10 @@ public static bool TryGetValueForPropertyChain(out TValue changeValue, o } var lastExpression = expressions[count - 1]; - changeValue = (TValue)GetValueFetcherOrThrow(lastExpression.GetMemberInfo())(current, lastExpression.GetArgumentsArray())!; + changeValue = + (TValue)GetValueFetcherOrThrow(lastExpression.GetMemberInfo())( + current, + lastExpression.GetArgumentsArray())!; return true; } @@ -282,7 +302,10 @@ public static bool TryGetValueForPropertyChain(out TValue changeValue, o /// element at the failing index and returns . /// [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static bool TryGetAllValuesForPropertyChain(out IObservedChange[] changeValues, object? current, IEnumerable expressionChain) // TODO: Create Test + public static bool TryGetAllValuesForPropertyChain( + out IObservedChange[] changeValues, + object? current, + IEnumerable expressionChain) { var expressions = MaterializeExpressions(expressionChain); var count = expressions.Length; @@ -291,7 +314,7 @@ public static bool TryGetAllValuesForPropertyChain(out IObservedChange + /// Based on a list of expressions, attempts to set the value of the last property in the chain, throwing on missing members. + /// + /// The type of the end value being set. + /// The object that starts the property chain. + /// A sequence of expressions that point to properties/fields. + /// The value to set on the last property in the chain. + /// True if the value was successfully set; otherwise false. + /// Thrown when expressionChain is empty. + /// Thrown when target is null and traversal is required. + /// Thrown when a required member is not settable. + [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + public static bool TrySetValueToPropertyChain( + object? target, + IEnumerable expressionChain, + TValue value) => + TrySetValueToPropertyChain(target, expressionChain, value, true); + /// /// Based on a list of expressions, attempts to set the value of the last property in the chain. /// @@ -333,29 +374,33 @@ public static bool TryGetAllValuesForPropertyChain(out IObservedChangeA sequence of expressions that point to properties/fields. /// The value to set on the last property in the chain. /// - /// If , throw when reflection members are missing; otherwise fail softly. + /// If true, throw when reflection members are missing; otherwise fail softly. /// - /// if the value was successfully set; otherwise . - /// Thrown when is empty. + /// True if the value was successfully set; otherwise false. + /// Thrown when expressionChain is empty. /// - /// Thrown when is and traversal is required. + /// Thrown when target is null and traversal is required. /// /// - /// Thrown when is and a required member is not settable. + /// Thrown when shouldThrow is true and a required member is not settable. /// /// /// Trimming note: this method may traverse arbitrary member chains represented by expressions; it is not possible /// to express a complete trimming contract locally. /// [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static bool TrySetValueToPropertyChain(object? target, IEnumerable expressionChain, TValue value, bool shouldThrow = true) // TODO: Create Test + public static bool TrySetValueToPropertyChain( + object? target, + IEnumerable expressionChain, + TValue value, + bool shouldThrow) { var expressions = MaterializeExpressions(expressionChain); var count = expressions.Length; if (count == 0) { - throw new InvalidOperationException("Expression chain must contain at least one element."); + throw new InvalidOperationException(EmptyExpressionChainMessage); } for (var i = 0; i < count - 1; i++) @@ -368,7 +413,9 @@ public static bool TrySetValueToPropertyChain(object? target, IEnumerabl if (getter is not null) { - target = getter(target ?? throw new ArgumentNullException(nameof(target)), expression.GetArgumentsArray()); + target = getter( + target ?? throw new ArgumentNullException(nameof(target)), + expression.GetArgumentsArray()); } } @@ -407,13 +454,12 @@ public static bool TrySetValueToPropertyChain(object? target, IEnumerabl /// Trimming note: resolving types by string name is inherently trimming-unfriendly unless additional metadata is preserved externally. /// [RequiresUnreferencedCode("Resolves types by name and loads assemblies; types may be trimmed.")] - public static Type? ReallyFindType(string? type, bool throwOnFailure) // TODO: Create Test + public static Type? ReallyFindType(string? type, bool throwOnFailure) { var cache = Volatile.Read(ref _typeCache); if (cache is null) { - // Create inside the RUC boundary to avoid analyzer warnings from static initialization. - var created = new MemoizingMRUCache( + MemoizingMRUCache created = new( static (typeName, _) => GetTypeHelper(typeName), 20); @@ -439,19 +485,20 @@ public static bool TrySetValueToPropertyChain(object? target, IEnumerabl /// [RequiresUnreferencedCode("Reflects over custom delegate Invoke signature; members may be trimmed.")] public static Type GetEventArgsTypeForEvent( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, - string? eventName) // TODO: Create Test + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type, + string? eventName) { ArgumentExceptionHelper.ThrowIfNull(type); var eventInfo = type.GetRuntimeEvent(eventName!); if (eventInfo is null || eventInfo.EventHandlerType is null) { - throw new Exception($"Couldn't find {type.FullName}.{eventName}"); + throw new InvalidOperationException($"Couldn't find {type.FullName}.{eventName}"); } - // Faster and allocation-free: do not enumerate runtime methods. - var invoke = eventInfo.EventHandlerType.GetMethod("Invoke") ?? throw new MissingMethodException(eventInfo.EventHandlerType.FullName, "Invoke"); + var invoke = eventInfo.EventHandlerType.GetMethod("Invoke") ?? + throw new MissingMethodException(eventInfo.EventHandlerType.FullName, "Invoke"); var parameters = invoke.GetParameters(); return parameters[1].ParameterType; } @@ -468,9 +515,10 @@ public static Type GetEventArgsTypeForEvent( /// public static void ThrowIfMethodsNotOverloaded( string callingTypeName, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.NonPublicMethods)] Type targetType, - params string[] methodsToCheck) // TODO: Create Test + params string[] methodsToCheck) { ArgumentExceptionHelper.ThrowIfNull(methodsToCheck); @@ -492,7 +540,7 @@ public static void ThrowIfMethodsNotOverloaded( if (found is null) { - throw new Exception($"Your class must implement {name} and call {callingTypeName}.{name}"); + throw new InvalidOperationException($"Your class must implement {name} and call {callingTypeName}.{name}"); } } } @@ -510,7 +558,10 @@ public static void ThrowIfMethodsNotOverloaded( /// cannot express a complete trimming contract locally. /// [RequiresUnreferencedCode("Inspects declared methods on a runtime type; members may be trimmed.")] - public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object targetObject, params string[] methodsToCheck) // TODO: Create Test + public static void ThrowIfMethodsNotOverloaded( + string callingTypeName, + object targetObject, + params string[] methodsToCheck) { ArgumentExceptionHelper.ThrowIfNull(targetObject); ArgumentExceptionHelper.ThrowIfNull(methodsToCheck); @@ -533,7 +584,8 @@ public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object ta if (found is null) { - throw new Exception($"Your class must implement {name} and call {callingTypeName}.{name}"); + throw new InvalidOperationException( + $"Your class must implement {name} and call {callingTypeName}.{name}"); } } } @@ -544,7 +596,7 @@ public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object ta /// The property information to check. /// if the property is static; otherwise . /// Thrown when is . - public static bool IsStatic(this PropertyInfo item) // TODO: Create Test + public static bool IsStatic(this PropertyInfo item) { ArgumentExceptionHelper.ThrowIfNull(item); @@ -566,13 +618,14 @@ public static bool IsStatic(this PropertyInfo item) // TODO: Create Test /// that may be trimmed; callers should preserve metadata for observed members. /// [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] - internal static IObservable ViewModelWhenAnyValue(TViewModel? viewModel, TView view, Expression? expression) + internal static IObservable ViewModelWhenAnyValue( + TViewModel? viewModel, + TView view, + Expression? expression) where TView : class, IViewFor where TViewModel : class => view.WhenAnyValue(x => x.ViewModel) - .Where(x => x is not null) - .Select(x => ((TViewModel?)x).WhenAnyDynamic(expression, y => y.Value)) - .Switch()!; + .SwitchSelect(x => ((TViewModel?)x).WhenAnyDynamic(expression, y => y.Value))!; /// /// Attempts to resolve a type name using @@ -625,24 +678,25 @@ private static Expression[] MaterializeExpressions(IEnumerable expre { ArgumentExceptionHelper.ThrowIfNull(expressionChain); - if (expressionChain is Expression[] arr) + switch (expressionChain) { - return arr; - } + case Expression[] arr: + return arr; + case ICollection coll: + { + if (coll.Count == 0) + { + return []; + } - if (expressionChain is ICollection coll) - { - if (coll.Count == 0) - { - return Array.Empty(); - } + var result = new Expression[coll.Count]; + coll.CopyTo(result, 0); + return result; + } - var result = new Expression[coll.Count]; - coll.CopyTo(result, 0); - return result; + default: + return [.. expressionChain]; } - - return expressionChain.ToArray(); } /// @@ -689,7 +743,7 @@ public CompiledPropertyChain(Expression[] expressionChain) if (expressionChain.Length == 0) { - throw new InvalidOperationException("Expression chain must contain at least one element."); + throw new InvalidOperationException(EmptyExpressionChainMessage); } _expressions = expressionChain; @@ -833,35 +887,38 @@ public CompiledPropertyChainSetter(Expression[] expressionChain) { ArgumentExceptionHelper.ThrowIfNull(expressionChain); - if (expressionChain.Length == 0) + switch (expressionChain.Length) { - throw new InvalidOperationException("Expression chain must contain at least one element."); - } - - if (expressionChain.Length == 1) - { - _parentGetters = Array.Empty>(); - _parentArguments = Array.Empty(); - } - else - { - var parentCount = expressionChain.Length - 1; - _parentGetters = new Func[parentCount]; - _parentArguments = new object?[]?[parentCount]; + case 0: + throw new InvalidOperationException(EmptyExpressionChainMessage); + case 1: + { + _parentGetters = []; + _parentArguments = []; + break; + } - for (var i = 0; i < parentCount; i++) - { - var expr = expressionChain[i]; - _parentGetters[i] = GetValueFetcherOrThrow(expr.GetMemberInfo()); - _parentArguments[i] = expr.GetArgumentsArray(); - } + default: + { + var parentCount = expressionChain.Length - 1; + _parentGetters = new Func[parentCount]; + _parentArguments = new object?[]?[parentCount]; + + for (var i = 0; i < parentCount; i++) + { + var expr = expressionChain[i]; + _parentGetters[i] = GetValueFetcherOrThrow(expr.GetMemberInfo()); + _parentArguments[i] = expr.GetArgumentsArray(); + } + + break; + } } - var lastExpr = expressionChain[expressionChain.Length - 1]; + var lastExpr = expressionChain[^1]; _setter = GetValueSetterForProperty(lastExpr.GetMemberInfo()); _setterArguments = lastExpr.GetArgumentsArray(); - // Preserve legacy-style message format used by OrThrow helpers (type + member name). var member = lastExpr.GetMemberInfo(); _unsettableMemberMessage = $"Type '{member?.DeclaringType}' must have a property '{member?.Name}'"; } @@ -897,7 +954,6 @@ public bool TrySetValue(TSource? source, TValue value, bool shouldThrow = true) current = _parentGetters[i](current, _parentArguments[i]); if (current is null) { - // Preserve Try* semantics: intermediate nulls soft-fail; do not synthesize exceptions. return false; } } diff --git a/src/ReactiveUI/Helpers/ArgumentExceptionHelper.cs b/src/ReactiveUI/Helpers/ArgumentExceptionHelper.cs index 5e052e2772..d9ebc55b68 100644 --- a/src/ReactiveUI/Helpers/ArgumentExceptionHelper.cs +++ b/src/ReactiveUI/Helpers/ArgumentExceptionHelper.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,12 +20,16 @@ internal static class ArgumentExceptionHelper /// /// The reference type argument to validate as non-null. /// The name of the parameter with which corresponds. - public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + public static void ThrowIfNull( + [NotNull] object? argument, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) { - if (argument is null) + if (argument is not null) { - throw new ArgumentNullException(paramName); + return; } + + throw new ArgumentNullException(paramName); } /// @@ -34,12 +38,17 @@ public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpres /// The reference type argument to validate as non-null. /// The exception message. /// The name of the parameter with which corresponds. - public static void ThrowIfNullWithMessage([NotNull] object? argument, string message, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + public static void ThrowIfNullWithMessage( + [NotNull] object? argument, + string message, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) { - if (argument is null) + if (argument is not null) { - throw new ArgumentNullException(paramName, message); + return; } + + throw new ArgumentNullException(paramName, message); } /// @@ -49,17 +58,21 @@ public static void ThrowIfNullWithMessage([NotNull] object? argument, string mes /// The name of the parameter with which corresponds. /// is null. /// is empty. - public static void ThrowIfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + public static void ThrowIfNullOrEmpty( + [NotNull] string? argument, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) { if (argument is null) { throw new ArgumentNullException(paramName); } - if (argument.Length == 0) + if (argument.Length != 0) { - throw new ArgumentException("The value cannot be an empty string.", paramName); + return; } + + throw new ArgumentException("The value cannot be an empty string.", paramName); } /// @@ -69,17 +82,23 @@ public static void ThrowIfNullOrEmpty([NotNull] string? argument, [CallerArgumen /// The name of the parameter with which corresponds. /// is null. /// is empty or consists only of white-space characters. - public static void ThrowIfNullOrWhiteSpace([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + public static void ThrowIfNullOrWhiteSpace( + [NotNull] string? argument, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) { if (argument is null) { throw new ArgumentNullException(paramName); } - if (string.IsNullOrWhiteSpace(argument)) + if (!string.IsNullOrWhiteSpace(argument)) { - throw new ArgumentException("The value cannot be an empty string or composed entirely of whitespace.", paramName); + return; } + + throw new ArgumentException( + "The value cannot be an empty string or composed entirely of whitespace.", + paramName); } /// @@ -89,24 +108,12 @@ public static void ThrowIfNullOrWhiteSpace([NotNull] string? argument, [CallerAr /// The name of the parameter with which corresponds. public static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(value))] string? paramName = null) { - if (value < 0) + if (value >= 0) { - throw new ArgumentOutOfRangeException(paramName, "The value cannot be negative."); + return; } - } - /// - /// Throws an if is less than or equal to . - /// - /// The argument to validate. - /// The value to compare with. - /// The name of the parameter with which corresponds. - public static void ThrowIfLessThanOrEqual(int value, int other, [CallerArgumentExpression(nameof(value))] string? paramName = null) - { - if (value <= other) - { - throw new ArgumentOutOfRangeException(paramName, $"The value cannot be less than or equal to {other}."); - } + throw new ArgumentOutOfRangeException(paramName, "The value cannot be negative."); } /// @@ -115,12 +122,17 @@ public static void ThrowIfLessThanOrEqual(int value, int other, [CallerArgumentE /// The condition to evaluate. /// The exception message. /// The name of the parameter with which corresponds. - public static void ThrowIf([DoesNotReturnIf(true)] bool condition, string message, [CallerArgumentExpression(nameof(condition))] string? paramName = null) + public static void ThrowIf( + [DoesNotReturnIf(true)] bool condition, + string message, + [CallerArgumentExpression(nameof(condition))] string? paramName = null) { - if (condition) + if (!condition) { - throw new ArgumentException(message, paramName); + return; } + + throw new ArgumentException(message, paramName); } /// @@ -130,10 +142,12 @@ public static void ThrowIf([DoesNotReturnIf(true)] bool condition, string messag /// The name of the parameter with which corresponds. public static void ThrowIfZero(int value, [CallerArgumentExpression(nameof(value))] string? paramName = null) { - if (value == 0) + if (value != 0) { - throw new ArgumentOutOfRangeException(paramName, "The value cannot be zero."); + return; } + + throw new ArgumentOutOfRangeException(paramName, "The value cannot be zero."); } /// @@ -141,102 +155,161 @@ public static void ThrowIfZero(int value, [CallerArgumentExpression(nameof(value /// /// The argument to validate. /// The name of the parameter with which corresponds. - public static void ThrowIfNegativeOrZero(int value, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfNegativeOrZero( + int value, + [CallerArgumentExpression(nameof(value))] string? paramName = null) { - if (value <= 0) + if (value > 0) { - throw new ArgumentOutOfRangeException(paramName, "The value cannot be negative or zero."); + return; } + + throw new ArgumentOutOfRangeException(paramName, "The value cannot be negative or zero."); } /// /// Throws an if is equal to . /// + /// The type of the arguments to compare. /// The argument to validate. /// The value to compare with. /// The name of the parameter with which corresponds. - public static void ThrowIfEqual(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfEqual( + T value, + T other, + [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IEquatable { - if (value.Equals(other)) + if (!value.Equals(other)) { - throw new ArgumentOutOfRangeException(paramName, $"The value cannot be equal to {other}."); + return; } + + throw new ArgumentOutOfRangeException(paramName, $"The value cannot be equal to {other}."); } /// /// Throws an if is not equal to . /// + /// The type of the arguments to compare. /// The argument to validate. /// The value to compare with. /// The name of the parameter with which corresponds. - public static void ThrowIfNotEqual(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfNotEqual( + T value, + T other, + [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IEquatable { - if (!value.Equals(other)) + if (value.Equals(other)) { - throw new ArgumentOutOfRangeException(paramName, $"The value must be equal to {other}."); + return; } + + throw new ArgumentOutOfRangeException(paramName, $"The value must be equal to {other}."); } /// /// Throws an if is greater than . /// + /// The type of the arguments to compare. /// The argument to validate. /// The value to compare with. /// The name of the parameter with which corresponds. - public static void ThrowIfGreaterThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfGreaterThan( + T value, + T other, + [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IComparable { - if (value.CompareTo(other) > 0) + if (value.CompareTo(other) <= 0) { - throw new ArgumentOutOfRangeException(paramName, $"The value cannot be greater than {other}."); + return; } + + throw new ArgumentOutOfRangeException(paramName, $"The value cannot be greater than {other}."); } /// /// Throws an if is greater than or equal to . /// + /// The type of the arguments to compare. /// The argument to validate. /// The value to compare with. /// The name of the parameter with which corresponds. - public static void ThrowIfGreaterThanOrEqual(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfGreaterThanOrEqual( + T value, + T other, + [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IComparable { - if (value.CompareTo(other) >= 0) + if (value.CompareTo(other) < 0) { - throw new ArgumentOutOfRangeException(paramName, $"The value cannot be greater than or equal to {other}."); + return; } + + throw new ArgumentOutOfRangeException(paramName, $"The value cannot be greater than or equal to {other}."); } /// /// Throws an if is less than . /// + /// The type of the arguments to compare. /// The argument to validate. /// The value to compare with. /// The name of the parameter with which corresponds. - public static void ThrowIfLessThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfLessThan( + T value, + T other, + [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IComparable { - if (value.CompareTo(other) < 0) + if (value.CompareTo(other) >= 0) { - throw new ArgumentOutOfRangeException(paramName, $"The value cannot be less than {other}."); + return; } + + throw new ArgumentOutOfRangeException(paramName, $"The value cannot be less than {other}."); } /// /// Throws an if is less than or equal to . /// + /// The type of the arguments to compare. /// The argument to validate. /// The value to compare with. /// The name of the parameter with which corresponds. - public static void ThrowIfLessThanOrEqual(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) + public static void ThrowIfLessThanOrEqual( + T value, + T other, + [CallerArgumentExpression(nameof(value))] string? paramName = null) where T : IComparable { - if (value.CompareTo(other) <= 0) + if (value.CompareTo(other) > 0) + { + return; + } + + throw new ArgumentOutOfRangeException(paramName, $"The value cannot be less than or equal to {other}."); + } + + /// + /// Throws an if is less than or equal to . + /// + /// The argument to validate. + /// The value to compare with. + /// The name of the parameter with which corresponds. + public static void ThrowIfLessThanOrEqual( + int value, + int other, + [CallerArgumentExpression(nameof(value))] string? paramName = null) + { + if (value > other) { - throw new ArgumentOutOfRangeException(paramName, $"The value cannot be less than or equal to {other}."); + return; } + + throw new ArgumentOutOfRangeException(paramName, $"The value cannot be less than or equal to {other}."); } /// @@ -245,12 +318,20 @@ public static void ThrowIfLessThanOrEqual(T value, T other, [CallerArgumentEx /// The expected type. /// The argument to validate. /// The name of the parameter with which corresponds. - public static void ThrowIfNotOfType([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static void ThrowIfNotOfType( + [NotNull] object? argument, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) { - if (argument is not T) + if (argument is T) { - throw new ArgumentException($"Argument must be of type {typeof(T).Name}.", paramName); + return; } + + throw new ArgumentException($"Argument must be of type {typeof(T).Name}.", paramName); } /// @@ -259,12 +340,16 @@ public static void ThrowIfNotOfType([NotNull] object? argument, [CallerArgume /// The struct type. /// The argument to validate. /// The name of the parameter with which corresponds. - public static void ThrowIfDefault(T argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + public static void ThrowIfDefault( + T argument, + [CallerArgumentExpression(nameof(argument))] string? paramName = null) where T : struct { - if (EqualityComparer.Default.Equals(argument, default)) + if (!EqualityComparer.Default.Equals(argument, default)) { - throw new ArgumentNullException(paramName); + return; } + + throw new ArgumentNullException(paramName); } } diff --git a/src/ReactiveUI/Interactions/IInteraction.cs b/src/ReactiveUI/Interactions/IInteraction.cs index 8d9dd650cc..e318239651 100644 --- a/src/ReactiveUI/Interactions/IInteraction.cs +++ b/src/ReactiveUI/Interactions/IInteraction.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interactions/IInteractionContext.cs b/src/ReactiveUI/Interactions/IInteractionContext.cs index ce52ffcb19..01be89fb38 100644 --- a/src/ReactiveUI/Interactions/IInteractionContext.cs +++ b/src/ReactiveUI/Interactions/IInteractionContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interactions/IOutputContext.cs b/src/ReactiveUI/Interactions/IOutputContext.cs index b85ed5dda2..725aac0832 100644 --- a/src/ReactiveUI/Interactions/IOutputContext.cs +++ b/src/ReactiveUI/Interactions/IOutputContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interactions/Interaction.cs b/src/ReactiveUI/Interactions/Interaction.cs index 43dad8fea2..41bdcb93aa 100644 --- a/src/ReactiveUI/Interactions/Interaction.cs +++ b/src/ReactiveUI/Interactions/Interaction.cs @@ -1,8 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -78,8 +83,17 @@ namespace ReactiveUI; /// public class Interaction(IScheduler? handlerScheduler = null) : IInteraction { + /// The ordered list of registered interaction handlers. private readonly List, IObservable>> _handlers = []; + + /// Lock object used to synchronize access to the handler list. + #if NET9_0_OR_GREATER + private readonly Lock _sync = new(); + #else private readonly object _sync = new(); + #endif + + /// The scheduler on which handlers are invoked. private readonly IScheduler _handlerScheduler = handlerScheduler ?? CurrentThreadScheduler.Instance; /// @@ -87,13 +101,13 @@ public IDisposable RegisterHandler(Action> { ArgumentExceptionHelper.ThrowIfNull(handler); + return RegisterHandlerCore(ContentHandler); + IObservable ContentHandler(IInteractionContext interaction) { handler(interaction); - return Observables.Unit; + return SingleValueObservable.Unit; } - - return RegisterHandlerCore(ContentHandler); } /// @@ -101,46 +115,37 @@ public IDisposable RegisterHandler(Func, Ta { ArgumentExceptionHelper.ThrowIfNull(handler); + return RegisterHandlerCore(ContentHandler); + + // Yield before invoking the async handler so it is not run inside the current scheduler + // trampoline (see #4351). IObservable ContentHandler(IInteractionContext interaction) => - Observable.FromAsync( - async () => - { - await YieldToCurrentContext(); - await handler(interaction).ConfigureAwait(false); - }); + new TaskUnitObservable(InvokeAsync(interaction)); - return RegisterHandlerCore(ContentHandler); + async Task InvokeAsync(IInteractionContext interaction) + { + await YieldToCurrentContext(); + await handler(interaction).ConfigureAwait(false); + } } /// - public IDisposable RegisterHandler(Func, IObservable> handler) + public IDisposable RegisterHandler( + Func, IObservable> handler) { ArgumentExceptionHelper.ThrowIfNull(handler); - IObservable ContentHandler(IInteractionContext context) => - Observable.FromAsync(() => Task.Run(() => handler(context))) - .SelectMany(result => result.Select(_ => Unit.Default)); - return RegisterHandlerCore(ContentHandler); + + IObservable ContentHandler(IInteractionContext context) => + new ToUnitObservable(handler(context)); } /// public virtual IObservable Handle(TInput input) { var context = GenerateContext(input); - - return Enumerable.Reverse(GetHandlers()) - .ToObservable() - .ObserveOn(_handlerScheduler) - .Select(handler => Observable.Defer(() => handler(context))) - .Concat() - .TakeWhile(_ => !context.IsHandled) - .SelectMany(_ => Observable.Empty()) - .Concat( - Observable.Defer( - () => context.IsHandled - ? Observable.Return(context.GetOutput()) - : Observable.Throw(new UnhandledInteractionException(this, input)))); + return new InteractionHandleObservable(GetHandlers(), context, _handlerScheduler, this, input); } /// @@ -162,24 +167,21 @@ protected Func, IObservable>[] GetHan /// /// The input that is being passed in. /// The interaction context. - protected virtual IOutputContext GenerateContext(TInput input) => new InteractionContext(input); + protected virtual IOutputContext GenerateContext(TInput input) => + new InteractionContext(input); - /// - /// Yields through the default task scheduler so asynchronous handlers are not invoked inside the current scheduler trampoline. - /// - /// A task that completes after the current scheduler trampoline has yielded. - private static Task YieldToCurrentContext() => Task.Run(static () => { }); + /// Yields once so asynchronous handlers are not invoked inside the current scheduler trampoline. + /// A task that completes after the current context has yielded. + private static async Task YieldToCurrentContext() => await Task.Yield(); - /// - /// Registers a normalized interaction handler. - /// + /// Registers a normalized interaction handler that produces a stream. /// The normalized handler. /// A disposable which unregisters the handler. - private IDisposable RegisterHandlerCore(Func, IObservable> contentHandler) + private ActionDisposable RegisterHandlerCore(Func, IObservable> contentHandler) { ArgumentExceptionHelper.ThrowIfNull(contentHandler); AddHandler(contentHandler); - return Disposable.Create(() => RemoveHandler(contentHandler)); + return new ActionDisposable(() => RemoveHandler(contentHandler)); } /// diff --git a/src/ReactiveUI/Interactions/InteractionContext.cs b/src/ReactiveUI/Interactions/InteractionContext.cs index e809874422..32b63e8e4f 100644 --- a/src/ReactiveUI/Interactions/InteractionContext.cs +++ b/src/ReactiveUI/Interactions/InteractionContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -39,9 +39,16 @@ namespace ReactiveUI; /// public sealed class InteractionContext : IOutputContext { + /// The output value set by the handler. private TOutput _output = default!; + + /// Indicates whether the output has been set (1) or not (0). private int _outputSet; + /// + /// Initializes a new instance of the class with the specified input. + /// + /// The input for the interaction. internal InteractionContext(TInput input) => Input = input; /// diff --git a/src/ReactiveUI/Interactions/UnhandledInteractionException.cs b/src/ReactiveUI/Interactions/UnhandledInteractionException.cs index f25176c4db..c38e4c24ed 100644 --- a/src/ReactiveUI/Interactions/UnhandledInteractionException.cs +++ b/src/ReactiveUI/Interactions/UnhandledInteractionException.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -19,6 +19,7 @@ namespace ReactiveUI; #endif public class UnhandledInteractionException : Exception { + /// The interaction instance that was not handled. [field: NonSerialized] private readonly Interaction? _interaction; diff --git a/src/ReactiveUI/Interfaces/IActivatableView.cs b/src/ReactiveUI/Interfaces/IActivatableView.cs index 8c261aa437..23ac8ec3da 100644 --- a/src/ReactiveUI/Interfaces/IActivatableView.cs +++ b/src/ReactiveUI/Interfaces/IActivatableView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -9,4 +9,8 @@ namespace ReactiveUI; /// Use this Interface when you want to mark a control as receiving View /// Activation when it doesn't have a backing ViewModel. /// +[SuppressMessage( + "Design", + "CA1040:Avoid empty interfaces", + Justification = "Marker interface for activatable views.")] public interface IActivatableView; diff --git a/src/ReactiveUI/Interfaces/IActivatableViewModel.cs b/src/ReactiveUI/Interfaces/IActivatableViewModel.cs index af8e6f627b..8fa2a7d4cd 100644 --- a/src/ReactiveUI/Interfaces/IActivatableViewModel.cs +++ b/src/ReactiveUI/Interfaces/IActivatableViewModel.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/ICanActivate.cs b/src/ReactiveUI/Interfaces/ICanActivate.cs index 6c9644dbb1..5945fd337c 100644 --- a/src/ReactiveUI/Interfaces/ICanActivate.cs +++ b/src/ReactiveUI/Interfaces/ICanActivate.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/Interfaces/ICanForceManualActivation.cs b/src/ReactiveUI/Interfaces/ICanForceManualActivation.cs index 4862748a40..c6b77afed2 100644 --- a/src/ReactiveUI/Interfaces/ICanForceManualActivation.cs +++ b/src/ReactiveUI/Interfaces/ICanForceManualActivation.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -14,6 +14,6 @@ internal interface ICanForceManualActivation /// /// Activates the view object. /// - /// If we are activating or not. - void Activate(bool activate); + /// If we are activating or not. + void Activate(bool isActivating); } diff --git a/src/ReactiveUI/Interfaces/ICreatesCommandBinding.cs b/src/ReactiveUI/Interfaces/ICreatesCommandBinding.cs index 8c5d044b04..7d8d5ca2d6 100644 --- a/src/ReactiveUI/Interfaces/ICreatesCommandBinding.cs +++ b/src/ReactiveUI/Interfaces/ICreatesCommandBinding.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -24,7 +24,14 @@ public interface ICreatesCommandBinding /// Determines if the host intends to use a custom event target. /// The type of the object to query for compatibility with command binding. /// A positive integer if binding is supported, or zero/a negative value if not supported. - int GetAffinityForObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] T>(bool hasEventTarget); + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + int GetAffinityForObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + T>(bool hasEventTarget); /// /// Bind an ICommand to a UI object, in the "default" way. The meaning @@ -43,10 +50,14 @@ public interface ICreatesCommandBinding /// Observable.Empty. /// An IDisposable which will disconnect the binding when disposed, or null if no binding was created. [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T>( - ICommand? command, - T? target, - IObservable commandParameter) + IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T>( + ICommand? command, + T? target, + IObservable commandParameter) where T : class; /// @@ -66,11 +77,15 @@ public interface ICreatesCommandBinding /// The event to bind to. /// An IDisposable which will disconnect the binding when disposed, or null if no binding was created. [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] IDisposable? BindCommandToObject( - ICommand? command, - T? target, - IObservable commandParameter, - string eventName) + ICommand? command, + T? target, + IObservable commandParameter, + string eventName) where T : class; /// @@ -87,7 +102,11 @@ public interface ICreatesCommandBinding /// /// Thrown when , , or is . /// - IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T, TEventArgs>( + IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T, TEventArgs>( ICommand? command, T? target, IObservable commandParameter, diff --git a/src/ReactiveUI/Interfaces/ICreatesCustomizedCommandRebinding.cs b/src/ReactiveUI/Interfaces/ICreatesCustomizedCommandRebinding.cs index 9ad4e5d442..cd71d2e66c 100644 --- a/src/ReactiveUI/Interfaces/ICreatesCustomizedCommandRebinding.cs +++ b/src/ReactiveUI/Interfaces/ICreatesCustomizedCommandRebinding.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs b/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs index 60fdbd2e85..4098c615ca 100644 --- a/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs +++ b/src/ReactiveUI/Interfaces/ICreatesObservableForProperty.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// @@ -15,8 +17,20 @@ namespace ReactiveUI; public interface ICreatesObservableForProperty : IEnableLogger { /// - /// Returns a positive integer when this instance supports for - /// the specified and . + /// Returns a positive integer when this instance supports GetNotificationForProperty for + /// the specified type and propertyName, assuming after-change notifications. + /// + /// The runtime type to query. + /// The property name to query. + /// + /// A positive integer if supported; zero or a negative value otherwise. + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + int GetAffinityForObject(Type type, string propertyName); + + /// + /// Returns a positive integer when this instance supports GetNotificationForProperty for + /// the specified type and propertyName. /// /// /// @@ -30,45 +44,79 @@ public interface ICreatesObservableForProperty : IEnableLogger /// The runtime type to query. /// The property name to query. /// - /// If , indicates the caller requests notifications before the property value changes. - /// If , indicates after-change notifications. + /// If true, indicates the caller requests notifications before the property value changes. + /// If false, indicates after-change notifications. /// /// /// A positive integer if supported; zero or a negative value otherwise. /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false); + int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged); + + /// + /// Subscribes to after-change notifications for the specified propertyName on sender. + /// + /// The object to observe. + /// The expression describing the observed member. + /// The property name to observe. + /// + /// An observable that produces an IObservedChange whenever the observed property changes. + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName); + + /// + /// Subscribes to change notifications for the specified propertyName on sender. + /// + /// The object to observe. + /// The expression describing the observed member. + /// The property name to observe. + /// + /// If true, signal before the property value changes; otherwise signal after the change. + /// + /// + /// An observable that produces an IObservedChange whenever the observed property changes. + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged); /// - /// Subscribes to change notifications for the specified on . + /// Subscribes to change notifications for the specified propertyName on sender. /// /// The object to observe. /// /// The expression describing the observed member. - /// This is typically a MemberExpression or an IndexExpression. + /// This is typically a MemberExpression or an IndexExpression. /// /// The property name to observe. /// - /// If , signal before the property value changes; otherwise signal after the change. + /// If true, signal before the property value changes; otherwise signal after the change. /// - /// If , warnings should not be logged. + /// If true, warnings should not be logged. /// - /// An observable that produces an whenever the observed property changes. - /// If observing is not possible for the specified value, implementations should return + /// An observable that produces an IObservedChange whenever the observed property changes. + /// If observing is not possible for the specified beforeChanged value, implementations should return /// an observable that never produces values. /// /// - /// Thrown when is not compatible with the observing mechanism implemented by the instance. + /// Thrown when sender is not compatible with the observing mechanism implemented by the instance. /// /// - /// The describes the observed member and is used to populate - /// instances emitted by the returned observable. + /// The expression describes the observed member and is used to populate + /// IObservedChange instances emitted by the returned observable. /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] IObservable> GetNotificationForProperty( object sender, Expression expression, string propertyName, - bool beforeChanged = false, - bool suppressWarnings = false); + bool beforeChanged, + bool suppressWarnings); } diff --git a/src/ReactiveUI/Interfaces/IHandleObservableErrors.cs b/src/ReactiveUI/Interfaces/IHandleObservableErrors.cs index 867676b9ff..73b0ea8285 100644 --- a/src/ReactiveUI/Interfaces/IHandleObservableErrors.cs +++ b/src/ReactiveUI/Interfaces/IHandleObservableErrors.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/IMessageBus.cs b/src/ReactiveUI/Interfaces/IMessageBus.cs index 4f378a09d3..175d81ea9a 100644 --- a/src/ReactiveUI/Interfaces/IMessageBus.cs +++ b/src/ReactiveUI/Interfaces/IMessageBus.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; + namespace ReactiveUI; /// @@ -20,6 +22,23 @@ namespace ReactiveUI; /// public interface IMessageBus : IEnableLogger { + /// + /// Registers a scheduler for the type, which may be specified at + /// runtime, and the contract. + /// + /// If a scheduler is already registered for the specified + /// runtime and contract, this will overwrite the existing + /// registration. + /// The type of the message to listen to. + /// The scheduler on which to post the + /// notifications for the specified type and contract. + /// CurrentThreadScheduler by default. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + void RegisterScheduler(IScheduler scheduler); + /// /// Registers a scheduler for the type, which may be specified at /// runtime, and the contract. @@ -34,7 +53,23 @@ public interface IMessageBus : IEnableLogger /// A unique string to distinguish messages with /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. - void RegisterScheduler(IScheduler scheduler, string? contract = null); + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + void RegisterScheduler(IScheduler scheduler, string? contract); + + /// + /// Listen provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage. + /// + /// The type of the message to listen to. + /// An observable sequence. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + IObservable Listen(); /// /// Listen provides an Observable that will fire whenever a Message is @@ -45,7 +80,25 @@ public interface IMessageBus : IEnableLogger /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. /// An observable sequence. - IObservable Listen(string? contract = null); + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + IObservable Listen(string? contract); + + /// + /// ListenIncludeLatest provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage and fire the + /// last provided Message immediately if applicable, or null. + /// + /// The type of the message to listen to. + /// An Observable representing the notifications posted to the + /// message bus. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + IObservable ListenIncludeLatest(); /// /// ListenIncludeLatest provides an Observable that will fire whenever a Message is @@ -58,7 +111,18 @@ public interface IMessageBus : IEnableLogger /// only used for one purpose, leave this as null. /// An Observable representing the notifications posted to the /// message bus. - IObservable ListenIncludeLatest(string? contract = null); + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + IObservable ListenIncludeLatest(string? contract); + + /// + /// Determines if a particular message Type is registered. + /// + /// The type of the message. + /// True if messages have been posted for this message Type. + bool IsRegistered(Type type); /// /// Determines if a particular message Type is registered. @@ -68,7 +132,18 @@ public interface IMessageBus : IEnableLogger /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. /// True if messages have been posted for this message Type. - bool IsRegistered(Type type, string? contract = null); + bool IsRegistered(Type type, string? contract); + + /// + /// Registers an Observable representing the stream of messages to send. + /// Another part of the code can then call Listen to retrieve this + /// Observable. + /// + /// The type of the message to listen to. + /// An Observable that will be subscribed to, and a + /// message sent out for each value provided. + /// A disposable. + IDisposable RegisterMessageSource(IObservable source); /// /// Registers an Observable representing the stream of messages to send. @@ -82,7 +157,17 @@ public interface IMessageBus : IEnableLogger /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. /// A disposable. - IDisposable RegisterMessageSource(IObservable source, string? contract = null); + IDisposable RegisterMessageSource(IObservable source, string? contract); + + /// + /// Sends a single message using the specified Type and contract. + /// Consider using RegisterMessageSource instead if you will be sending + /// messages in response to other changes such as property changes + /// or events. + /// + /// The type of the message to send. + /// The actual message to send. + void SendMessage(T message); /// /// Sends a single message using the specified Type and contract. @@ -95,5 +180,5 @@ public interface IMessageBus : IEnableLogger /// A unique string to distinguish messages with /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. - void SendMessage(T message, string? contract = null); + void SendMessage(T message, string? contract); } diff --git a/src/ReactiveUI/Interfaces/IObservedChange.cs b/src/ReactiveUI/Interfaces/IObservedChange.cs index 73d058c5fa..9dbd8c6075 100644 --- a/src/ReactiveUI/Interfaces/IObservedChange.cs +++ b/src/ReactiveUI/Interfaces/IObservedChange.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/Interfaces/IPlatformOperations.cs b/src/ReactiveUI/Interfaces/IPlatformOperations.cs index 8378e87e8c..899f243004 100644 --- a/src/ReactiveUI/Interfaces/IPlatformOperations.cs +++ b/src/ReactiveUI/Interfaces/IPlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/IPropertyBindingHook.cs b/src/ReactiveUI/Interfaces/IPropertyBindingHook.cs index 3ad6089850..9adf7f51f0 100644 --- a/src/ReactiveUI/Interfaces/IPropertyBindingHook.cs +++ b/src/ReactiveUI/Interfaces/IPropertyBindingHook.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/IReactiveNotifyPropertyChanged.cs b/src/ReactiveUI/Interfaces/IReactiveNotifyPropertyChanged.cs index b393c3d29a..7c25acf303 100644 --- a/src/ReactiveUI/Interfaces/IReactiveNotifyPropertyChanged.cs +++ b/src/ReactiveUI/Interfaces/IReactiveNotifyPropertyChanged.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/IReactivePropertyChangedEventArgs.cs b/src/ReactiveUI/Interfaces/IReactivePropertyChangedEventArgs.cs index 361e4bd590..3218152914 100644 --- a/src/ReactiveUI/Interfaces/IReactivePropertyChangedEventArgs.cs +++ b/src/ReactiveUI/Interfaces/IReactivePropertyChangedEventArgs.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/IRegistrar.cs b/src/ReactiveUI/Interfaces/IRegistrar.cs index 55860fb1fb..62ab69a44e 100644 --- a/src/ReactiveUI/Interfaces/IRegistrar.cs +++ b/src/ReactiveUI/Interfaces/IRegistrar.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -12,6 +12,15 @@ namespace ReactiveUI; /// public interface IRegistrar { + /// + /// Registers a constant value for a service type. The factory function is called once + /// and the result is registered as a singleton. + /// + /// The service type to register. + /// A factory function that creates the service instance. + void RegisterConstant(Func factory) + where TService : class; + /// /// Registers a constant value for a service type. The factory function is called once /// and the result is registered as a singleton. @@ -19,7 +28,18 @@ public interface IRegistrar /// The service type to register. /// A factory function that creates the service instance. /// An optional contract name for multiple registrations of the same type. - void RegisterConstant(Func factory, string? contract = null) + void RegisterConstant(Func factory, string? contract) + where TService : class; + + /// + /// Registers a lazy singleton for a service type. The factory function is called + /// the first time the service is resolved, and the same instance is returned for all subsequent resolutions. + /// + /// The service type to register. + /// A factory function that creates the service instance. + void RegisterLazySingleton< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TService>( + Func factory) where TService : class; /// @@ -29,7 +49,19 @@ void RegisterConstant(Func factory, string? contract = null) /// The service type to register. /// A factory function that creates the service instance. /// An optional contract name for multiple registrations of the same type. - void RegisterLazySingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TService>(Func factory, string? contract = null) + void RegisterLazySingleton< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TService>( + Func factory, + string? contract) + where TService : class; + + /// + /// Registers a factory for a service type. The factory function is called each time + /// the service is resolved, creating a new instance. + /// + /// The service type to register. + /// A factory function that creates the service instance. + void Register(Func factory) where TService : class; /// @@ -39,6 +71,6 @@ void RegisterConstant(Func factory, string? contract = null) /// The service type to register. /// A factory function that creates the service instance. /// An optional contract name for multiple registrations of the same type. - void Register(Func factory, string? contract = null) + void Register(Func factory, string? contract) where TService : class; } diff --git a/src/ReactiveUI/Interfaces/IRoutableViewModel.cs b/src/ReactiveUI/Interfaces/IRoutableViewModel.cs index 5e5894ed75..d0e6aad5cd 100644 --- a/src/ReactiveUI/Interfaces/IRoutableViewModel.cs +++ b/src/ReactiveUI/Interfaces/IRoutableViewModel.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -33,9 +33,9 @@ public interface IRoutableViewModel : IReactiveObject /// /// Gets a string token representing the current view model, such as "login" or "user". /// -#pragma warning disable CA1056 // URI-like properties should not be strings +#pragma warning disable CA1056 string? UrlPathSegment { get; } -#pragma warning restore CA1056 // URI-like properties should not be strings +#pragma warning restore CA1056 /// /// Gets the instance that hosts this view model. Use this reference to access the diff --git a/src/ReactiveUI/Interfaces/IScreen.cs b/src/ReactiveUI/Interfaces/IScreen.cs index bf1a95261a..fe31313a66 100644 --- a/src/ReactiveUI/Interfaces/IScreen.cs +++ b/src/ReactiveUI/Interfaces/IScreen.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/ISuspensionDriver.cs b/src/ReactiveUI/Interfaces/ISuspensionDriver.cs index 889d5fe487..be3470eea5 100644 --- a/src/ReactiveUI/Interfaces/ISuspensionDriver.cs +++ b/src/ReactiveUI/Interfaces/ISuspensionDriver.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; using System.Text.Json.Serialization.Metadata; namespace ReactiveUI; @@ -23,25 +24,6 @@ namespace ReactiveUI; /// public interface ISuspensionDriver { - /// - /// Loads the application state from persistent storage. - /// - /// - /// An observable that produces the deserialized application state - /// (or ). - /// - /// - /// This member typically relies on reflection-based serialization and is not - /// trimming or AOT friendly. - /// - [RequiresUnreferencedCode( - "Implementations commonly use reflection-based serialization. " + - "Prefer LoadState(JsonTypeInfo) for trimming or AOT scenarios.")] - [RequiresDynamicCode( - "Implementations commonly use reflection-based serialization. " + - "Prefer LoadState(JsonTypeInfo) for trimming or AOT scenarios.")] - IObservable LoadState(); - /// /// Saves the application state to persistent storage. /// @@ -62,6 +44,20 @@ public interface ISuspensionDriver "Prefer SaveState(T, JsonTypeInfo) for trimming or AOT scenarios.")] IObservable SaveState(T state); + /// + /// Saves application state to persistent storage using + /// source-generated System.Text.Json metadata. + /// + /// The state type. + /// The state to persist. + /// + /// The source-generated metadata for . + /// + /// + /// An observable that completes when persistence succeeds. + /// + IObservable SaveState(T state, JsonTypeInfo typeInfo); + /// /// Loads application state from persistent storage using /// source-generated System.Text.Json metadata. @@ -77,18 +73,23 @@ public interface ISuspensionDriver IObservable LoadState(JsonTypeInfo typeInfo); /// - /// Saves application state to persistent storage using - /// source-generated System.Text.Json metadata. + /// Loads the application state from persistent storage. /// - /// The state type. - /// The state to persist. - /// - /// The source-generated metadata for . - /// /// - /// An observable that completes when persistence succeeds. + /// An observable that produces the deserialized application state + /// (or ). /// - IObservable SaveState(T state, JsonTypeInfo typeInfo); + /// + /// This member typically relies on reflection-based serialization and is not + /// trimming or AOT friendly. + /// + [RequiresUnreferencedCode( + "Implementations commonly use reflection-based serialization. " + + "Prefer LoadState(JsonTypeInfo) for trimming or AOT scenarios.")] + [RequiresDynamicCode( + "Implementations commonly use reflection-based serialization. " + + "Prefer LoadState(JsonTypeInfo) for trimming or AOT scenarios.")] + IObservable LoadState(); /// /// Invalidates the persisted application state diff --git a/src/ReactiveUI/Interfaces/ISuspensionHost.cs b/src/ReactiveUI/Interfaces/ISuspensionHost.cs index 50c24d437c..82c7dabdc8 100644 --- a/src/ReactiveUI/Interfaces/ISuspensionHost.cs +++ b/src/ReactiveUI/Interfaces/ISuspensionHost.cs @@ -1,20 +1,11 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; + namespace ReactiveUI; -/* Nicked from http://caliburnmicro.codeplex.com/wikipage?title=Working%20with%20Windows%20Phone%207%20v1.1 - * - * Launching - Occurs when a fresh instance of the application is launching. - * Activated - Occurs when a previously paused/tombstoned app is resumed/resurrected. - * Deactivated - Occurs when the application is being paused or tombstoned. - * Closing - Occurs when the application is closing. - * Continuing - Occurs when the app is continuing from a temporarily paused state. - * Continued - Occurs after the app has continued from a temporarily paused state. - * Resurrecting - Occurs when the app is "resurrecting" from a tombstoned state. - * Resurrected - Occurs after the app has "resurrected" from a tombstoned state. -*/ /// /// ISuspensionHost represents a standardized version of the events that the diff --git a/src/ReactiveUI/Interfaces/ISuspensionHost{TAppState}.cs b/src/ReactiveUI/Interfaces/ISuspensionHost{TAppState}.cs index a1dd922d8d..6ec283cec8 100644 --- a/src/ReactiveUI/Interfaces/ISuspensionHost{TAppState}.cs +++ b/src/ReactiveUI/Interfaces/ISuspensionHost{TAppState}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/IViewFor.cs b/src/ReactiveUI/Interfaces/IViewFor.cs index 51f2a64de2..dc1dd458cc 100644 --- a/src/ReactiveUI/Interfaces/IViewFor.cs +++ b/src/ReactiveUI/Interfaces/IViewFor.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -23,7 +23,7 @@ public interface IViewFor : IActivatableView object? ViewModel { get; set; } } -#pragma warning disable SA1402 // File may only contain a single type +#pragma warning disable SA1402 /// /// Implement this interface on views to participate in ReactiveUI routing, activation, and binding. /// @@ -52,7 +52,7 @@ public interface IViewFor : IActivatableView /// /// public interface IViewFor : IViewFor -#pragma warning restore SA1402 // File may only contain a single type +#pragma warning restore SA1402 where T : class { /// diff --git a/src/ReactiveUI/Interfaces/IViewLocator.cs b/src/ReactiveUI/Interfaces/IViewLocator.cs index f15a6c68b2..23cdaebe71 100644 --- a/src/ReactiveUI/Interfaces/IViewLocator.cs +++ b/src/ReactiveUI/Interfaces/IViewLocator.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -42,22 +42,51 @@ namespace ReactiveUI; /// public interface IViewLocator : IEnableLogger { + /// + /// Resolves a view for a view model type known at compile time. Fully AOT-compatible. + /// + /// The view model type to resolve a view for. + /// The resolved view or null when no registration is available. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + IViewFor? ResolveView() + where TViewModel : class; + /// /// Resolves a view for a view model type known at compile time. Fully AOT-compatible. /// /// The view model type to resolve a view for. /// Optional contract allowing multiple view registrations per view model. - /// The resolved view or when no registration is available. - IViewFor? ResolveView(string? contract = null) + /// The resolved view or null when no registration is available. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + IViewFor? ResolveView(string? contract) where TViewModel : class; + /// + /// Resolves a view for a view model instance using runtime type information. + /// + /// The view model instance to resolve a view for. + /// The resolved view or null when no registration is available. + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "Trimming can't validate that the requirements of those annotations are met.")] + IViewFor? ResolveView(object? instance); + /// /// Resolves a view for a view model instance using runtime type information. /// /// The view model instance to resolve a view for. /// Optional contract allowing multiple view registrations per view model. - /// The resolved view or when no registration is available. - [RequiresUnreferencedCode("This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] - [RequiresDynamicCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] - IViewFor? ResolveView(object? instance, string? contract = null); + /// The resolved view or null when no registration is available. + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "Trimming can't validate that the requirements of those annotations are met.")] + IViewFor? ResolveView(object? instance, string? contract); } diff --git a/src/ReactiveUI/Interfaces/IViewModule.cs b/src/ReactiveUI/Interfaces/IViewModule.cs index 756c609b9b..4d6197fdec 100644 --- a/src/ReactiveUI/Interfaces/IViewModule.cs +++ b/src/ReactiveUI/Interfaces/IViewModule.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/IWantsToRegisterStuff.cs b/src/ReactiveUI/Interfaces/IWantsToRegisterStuff.cs index 745678ecc2..6e27b90abc 100644 --- a/src/ReactiveUI/Interfaces/IWantsToRegisterStuff.cs +++ b/src/ReactiveUI/Interfaces/IWantsToRegisterStuff.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Interfaces/ObservedChange.cs b/src/ReactiveUI/Interfaces/ObservedChange.cs index 0266c5debb..e4db9a5603 100644 --- a/src/ReactiveUI/Interfaces/ObservedChange.cs +++ b/src/ReactiveUI/Interfaces/ObservedChange.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + namespace ReactiveUI; /// @@ -16,7 +18,8 @@ namespace ReactiveUI; /// The sender. /// Expression describing the member. /// The value. -public class ObservedChange(TSender sender, Expression? expression, TValue value) : IObservedChange +public class ObservedChange(TSender sender, Expression? expression, TValue value) + : IObservedChange { /// public TSender Sender { get; } = sender; diff --git a/src/ReactiveUI/Interfaces/ReactivePropertyChangedEventArgs.cs b/src/ReactiveUI/Interfaces/ReactivePropertyChangedEventArgs.cs index df2c3b80cc..2aa23e712c 100644 --- a/src/ReactiveUI/Interfaces/ReactivePropertyChangedEventArgs.cs +++ b/src/ReactiveUI/Interfaces/ReactivePropertyChangedEventArgs.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace ReactiveUI; /// @@ -15,7 +17,8 @@ namespace ReactiveUI; /// /// The sender. /// Name of the property. -public class ReactivePropertyChangedEventArgs(TSender sender, string propertyName) : PropertyChangedEventArgs(propertyName), IReactivePropertyChangedEventArgs +public class ReactivePropertyChangedEventArgs(TSender sender, string propertyName) + : PropertyChangedEventArgs(propertyName), IReactivePropertyChangedEventArgs { /// /// Gets the sender which triggered the property changed event. diff --git a/src/ReactiveUI/Interfaces/ReactivePropertyChangingEventArgs.cs b/src/ReactiveUI/Interfaces/ReactivePropertyChangingEventArgs.cs index ce85ddf32e..2bda1fb094 100644 --- a/src/ReactiveUI/Interfaces/ReactivePropertyChangingEventArgs.cs +++ b/src/ReactiveUI/Interfaces/ReactivePropertyChangingEventArgs.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace ReactiveUI; /// @@ -14,7 +16,8 @@ namespace ReactiveUI; /// /// The sender. /// Name of the property. -public class ReactivePropertyChangingEventArgs(TSender sender, string? propertyName) : PropertyChangingEventArgs(propertyName), IReactivePropertyChangedEventArgs +public class ReactivePropertyChangingEventArgs(TSender sender, string? propertyName) + : PropertyChangingEventArgs(propertyName), IReactivePropertyChangedEventArgs { /// /// Gets the sender which triggered the Reactive property changed event. diff --git a/src/ReactiveUI/Internal/Broadcaster.cs b/src/ReactiveUI/Internal/Broadcaster.cs new file mode 100644 index 0000000000..d93cb609da --- /dev/null +++ b/src/ReactiveUI/Internal/Broadcaster.cs @@ -0,0 +1,157 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A lean broadcast helper used in place of a general subject. The observer set is held in a single +/// reference field — for none, the lone for one, and a +/// copy-on-write array for two or more — so the common one-subscriber case +/// allocates nothing. As a value type it lives inline in its owner, adding no allocation of its own. +/// +/// +/// The owner serializes / through its own gate (those mutate the +/// observer set copy-on-write). Delivery via / / +/// is lock-free: it takes a single snapshot of the field and delivers against it, so the +/// owner's gate is never shared with — or held during — delivery. +/// +/// The broadcast element type. +internal record struct Broadcaster +{ + /// + /// The observer set: for none, an for exactly one, or a + /// copy-on-write array for two or more. Published with release semantics under the + /// owner's gate and read with acquire semantics during delivery. + /// + private object? _observers; + + /// Gets a value indicating whether any observer is currently subscribed. + public readonly bool HasObservers => _observers is not null; + + /// + /// Adds an observer. Must be called under the owner's gate. + /// + /// The observer to add. + public void Add(IObserver observer) + { + if (_observers is IObserver[] many) + { + var copy = new IObserver[many.Length + 1]; + Array.Copy(many, copy, many.Length); + copy[many.Length] = observer; + Volatile.Write(ref _observers, copy); + } + else if (_observers is IObserver single) + { + Volatile.Write(ref _observers, new[] { single, observer }); + } + else + { + Volatile.Write(ref _observers, observer); + } + } + + /// + /// Removes an observer if present. Must be called under the owner's gate. + /// + /// The observer to remove. + public void Remove(IObserver observer) + { + if (ReferenceEquals(_observers, observer)) + { + Volatile.Write(ref _observers, null); + return; + } + + if (_observers is not IObserver[] many) + { + return; + } + + var index = Array.IndexOf(many, observer); + if (index < 0) + { + return; + } + + if (many.Length == 2) + { + Volatile.Write(ref _observers, many[index == 0 ? 1 : 0]); + return; + } + + var copy = new IObserver[many.Length - 1]; + for (var i = 0; i < index; i++) + { + copy[i] = many[i]; + } + + for (var i = index + 1; i < many.Length; i++) + { + copy[i - 1] = many[i]; + } + + Volatile.Write(ref _observers, copy); + } + + /// + /// Broadcasts a value to every current observer. Lock-free: delivers against a snapshot. + /// + /// The value to broadcast. + public void Next(T value) + { + var snapshot = Volatile.Read(ref _observers); + if (snapshot is IObserver one) + { + one.OnNext(value); + } + else if (snapshot is IObserver[] many) + { + for (var i = 0; i < many.Length; i++) + { + many[i].OnNext(value); + } + } + } + + /// + /// Broadcasts an error to every current observer. Lock-free: delivers against a snapshot. + /// + /// The error to broadcast. + public void Error(Exception errorException) + { + var snapshot = Volatile.Read(ref _observers); + if (snapshot is IObserver one) + { + one.OnError(errorException); + } + else if (snapshot is IObserver[] many) + { + for (var i = 0; i < many.Length; i++) + { + many[i].OnError(errorException); + } + } + } + + /// + /// Broadcasts completion to every current observer. Lock-free: delivers against a snapshot. + /// + public void Completed() + { + var snapshot = Volatile.Read(ref _observers); + if (snapshot is IObserver one) + { + one.OnCompleted(); + } + else if (snapshot is IObserver[] many) + { + for (var i = 0; i < many.Length; i++) + { + many[i].OnCompleted(); + } + } + } +} diff --git a/src/ReactiveUI/Internal/DelegateObserver.cs b/src/ReactiveUI/Internal/DelegateObserver.cs new file mode 100644 index 0000000000..39fd71e698 --- /dev/null +++ b/src/ReactiveUI/Internal/DelegateObserver.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Delegate-backed observer used internally where a one-shot anonymous subscriber is convenient. +/// +/// The element type. +/// Per-value callback. +/// Error callback. +/// Completion callback. +internal sealed class DelegateObserver( + Action onNext, + Action? onError = null, + Action? onCompleted = null) : IObserver +{ + /// + public void OnNext(T value) => onNext(value); + + /// + public void OnError(Exception error) => onError?.Invoke(error); + + /// + public void OnCompleted() => onCompleted?.Invoke(); +} diff --git a/src/ReactiveUI/Internal/Disposables/ActionDisposable.cs b/src/ReactiveUI/Internal/Disposables/ActionDisposable.cs new file mode 100644 index 0000000000..9ab25149ac --- /dev/null +++ b/src/ReactiveUI/Internal/Disposables/ActionDisposable.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// An that runs the supplied exactly once on +/// . Replaces Disposable.Create(Action). +/// +internal sealed class ActionDisposable : IDisposable +{ + /// The action to run once on dispose; nulled out after it runs. + private Action? _action; + + /// + /// Initializes a new instance of the class. + /// + /// The action to invoke once on dispose. + public ActionDisposable(Action action) => _action = action; + + /// + public void Dispose() => Interlocked.Exchange(ref _action, null)?.Invoke(); +} diff --git a/src/ReactiveUI/Internal/Disposables/DisposableBag.cs b/src/ReactiveUI/Internal/Disposables/DisposableBag.cs new file mode 100644 index 0000000000..1562bdc5e5 --- /dev/null +++ b/src/ReactiveUI/Internal/Disposables/DisposableBag.cs @@ -0,0 +1,174 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A small composite-disposable replacement specialised for the common 2-slot +/// "subscription + sink" pair found throughout this codebase. Avoids the +/// backing field of +/// System.Reactive.Disposables.CompositeDisposable. Replaces +/// CompositeDisposable on internal code paths. +/// +/// +/// The first two added entries are stored inline. A third or later entry causes a fall-back +/// to a heap-allocated array. Disposal is idempotent and disposes every contained entry, +/// in registration order, exactly once. +/// +internal sealed class DisposableBag : IDisposable +{ + /// Starting capacity of the overflow array once the two inline slots are taken. + private const int OverflowInitialCapacity = 2; + + /// Growth factor applied when the overflow array is full. + private const int OverflowGrowthFactor = 2; + +#if NET9_0_OR_GREATER + /// Guards the slots, the overflow array, and the disposed flag. + private readonly Lock _gate = new(); +#else + /// Guards the slots, the overflow array, and the disposed flag. + private readonly object _gate = new(); +#endif + + /// The first inline disposable slot. + private IDisposable? _slot0; + + /// The second inline disposable slot. + private IDisposable? _slot1; + + /// Heap-allocated overflow for the third and later entries. + private IDisposable[]? _overflow; + + /// The number of entries currently held in . + private int _overflowCount; + + /// Whether the bag has been disposed. + private bool _disposed; + + /// + /// Initializes a new instance of the class. + /// + public DisposableBag() + { + } + + /// + /// Initializes a new instance of the class with two pre-populated slots. + /// + /// The first disposable. + /// The second disposable. + public DisposableBag(IDisposable first, IDisposable second) + { + _slot0 = first; + _slot1 = second; + } + + /// + /// Initializes a new instance of the class with three pre-populated slots. + /// + /// The first disposable. + /// The second disposable. + /// The third disposable. + public DisposableBag(IDisposable first, IDisposable second, IDisposable third) + { + _slot0 = first; + _slot1 = second; + _overflow = new IDisposable[OverflowInitialCapacity]; + _overflow[0] = third; + _overflowCount = 1; + } + + /// + /// Adds a disposable to the bag. If the bag is already disposed, the supplied + /// disposable is disposed immediately. + /// + /// The disposable to add. + public void Add(IDisposable disposable) + { + if (disposable is null) + { + return; + } + + var disposeNow = false; + lock (_gate) + { + if (_disposed) + { + disposeNow = true; + } + else if (_slot0 is null) + { + _slot0 = disposable; + } + else if (_slot1 is null) + { + _slot1 = disposable; + } + else + { + if (_overflow is null) + { + _overflow = new IDisposable[OverflowInitialCapacity]; + } + else if (_overflowCount == _overflow.Length) + { + var grown = new IDisposable[_overflow.Length * OverflowGrowthFactor]; + Array.Copy(_overflow, grown, _overflowCount); + _overflow = grown; + } + + _overflow[_overflowCount++] = disposable; + } + } + + if (!disposeNow) + { + return; + } + + disposable.Dispose(); + } + + /// + public void Dispose() + { + IDisposable? s0; + IDisposable? s1; + IDisposable[]? overflow; + int overflowCount; + + lock (_gate) + { + if (_disposed) + { + return; + } + + _disposed = true; + s0 = _slot0; + s1 = _slot1; + overflow = _overflow; + overflowCount = _overflowCount; + _slot0 = null; + _slot1 = null; + _overflow = null; + _overflowCount = 0; + } + + s0?.Dispose(); + s1?.Dispose(); + if (overflow is null) + { + return; + } + + for (var i = 0; i < overflowCount; i++) + { + overflow[i].Dispose(); + } + } +} diff --git a/src/ReactiveUI/Internal/Disposables/DisposableSlotHelper.cs b/src/ReactiveUI/Internal/Disposables/DisposableSlotHelper.cs new file mode 100644 index 0000000000..e504ed8c96 --- /dev/null +++ b/src/ReactiveUI/Internal/Disposables/DisposableSlotHelper.cs @@ -0,0 +1,94 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Shared lock-free slot mechanics for the single-slot reassignable disposables +/// ( and ). The disposed +/// flag is an (0 = open, 1 = disposed) driven through +/// so a race between assignment and disposal always leaves +/// the most recently assigned value disposed. +/// +internal static class DisposableSlotHelper +{ + /// + /// Assigns into the slot without disposing the previous value + /// (multiple-assignment semantics). If the slot is already disposed, + /// is disposed immediately. + /// + /// The slot field. + /// The disposed flag field (0 = open, 1 = disposed). + /// The disposable to assign. + public static void AssignWithoutDisposingPrevious(ref IDisposable? current, ref int disposed, IDisposable? value) + { + if (Volatile.Read(ref disposed) != 0) + { + value?.Dispose(); + return; + } + + Volatile.Write(ref current, value); + + if (Volatile.Read(ref disposed) == 0) + { + return; + } + + if (!ReferenceEquals(Interlocked.Exchange(ref current, null), value)) + { + return; + } + + value?.Dispose(); + } + + /// + /// Swaps into the slot and disposes the value it replaced + /// (serial semantics). If the slot is already disposed, is + /// disposed immediately. + /// + /// The slot field. + /// The disposed flag field (0 = open, 1 = disposed). + /// The disposable to assign. + public static void SwapAndDisposePrevious(ref IDisposable? current, ref int disposed, IDisposable? value) + { + if (Volatile.Read(ref disposed) != 0) + { + value?.Dispose(); + return; + } + + var previous = Interlocked.Exchange(ref current, value); + previous?.Dispose(); + + if (Volatile.Read(ref disposed) == 0) + { + return; + } + + if (!ReferenceEquals(Interlocked.Exchange(ref current, null), value)) + { + return; + } + + value?.Dispose(); + } + + /// + /// Marks the slot disposed exactly once and disposes the current value. + /// + /// The slot field. + /// The disposed flag field (0 = open, 1 = disposed). + public static void TryDispose(ref IDisposable? current, ref int disposed) + { + if (Interlocked.Exchange(ref disposed, 1) != 0) + { + return; + } + + Interlocked.Exchange(ref current, null)?.Dispose(); + } +} diff --git a/src/ReactiveUI/Internal/Disposables/EmptyDisposable.cs b/src/ReactiveUI/Internal/Disposables/EmptyDisposable.cs new file mode 100644 index 0000000000..2cac8d380f --- /dev/null +++ b/src/ReactiveUI/Internal/Disposables/EmptyDisposable.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A no-op singleton used in place of Disposable.Empty. +/// +internal sealed class EmptyDisposable : IDisposable +{ + /// + /// The shared singleton instance. + /// + public static readonly EmptyDisposable Instance = new(); + + /// + /// Initializes a new instance of the class. + /// + private EmptyDisposable() + { + } + + /// + public void Dispose() + { + } +} diff --git a/src/ReactiveUI/Internal/Disposables/MutableDisposable.cs b/src/ReactiveUI/Internal/Disposables/MutableDisposable.cs new file mode 100644 index 0000000000..3a986aa068 --- /dev/null +++ b/src/ReactiveUI/Internal/Disposables/MutableDisposable.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A disposable holder whose inner disposable can be re-assigned. The previous inner +/// disposable is NOT disposed when replaced (in contrast to ). +/// Once this object is disposed, any subsequently assigned inner disposable is disposed +/// immediately. Replaces MultipleAssignmentDisposable. +/// +internal sealed class MutableDisposable : IDisposable +{ + /// The current inner disposable. + private IDisposable? _current; + + /// Disposed flag (0 = open, 1 = disposed), driven through . + private int _disposed; + + /// + /// Gets or sets the current inner disposable. + /// + public IDisposable? Disposable + { + get => Volatile.Read(ref _current); + set => DisposableSlotHelper.AssignWithoutDisposingPrevious(ref _current, ref _disposed, value); + } + + /// + public void Dispose() => DisposableSlotHelper.TryDispose(ref _current, ref _disposed); +} diff --git a/src/ReactiveUI/Internal/Disposables/OnceDisposable.cs b/src/ReactiveUI/Internal/Disposables/OnceDisposable.cs new file mode 100644 index 0000000000..fbd0ba1e4c --- /dev/null +++ b/src/ReactiveUI/Internal/Disposables/OnceDisposable.cs @@ -0,0 +1,72 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A disposable holder whose inner disposable can be set exactly once. +/// Replaces SingleAssignmentDisposable. Subsequent assignments throw +/// ; if the holder has been disposed before +/// assignment, the supplied disposable is disposed immediately and no exception is thrown. +/// +internal sealed class OnceDisposable : IDisposable +{ + /// Sentinel stored in the slot once the holder has been disposed. + private static readonly IDisposable DisposedSentinel = EmptyDisposable.Instance; + + /// The assigned inner disposable, or the disposed sentinel after disposal. + private IDisposable? _current; + + /// + /// Gets a value indicating whether a disposable has been assigned. + /// + public bool IsAssigned => Volatile.Read(ref _current) is not null; + + /// + /// Gets a value indicating whether the holder has been disposed. + /// + public bool IsDisposed => ReferenceEquals(Volatile.Read(ref _current), DisposedSentinel); + + /// + /// Gets or sets the inner disposable. Setting more than once throws. + /// + public IDisposable? Disposable + { + get + { + var current = Volatile.Read(ref _current); + return ReferenceEquals(current, DisposedSentinel) ? null : current; + } + + set + { + var previous = Interlocked.CompareExchange(ref _current, value, null); + if (previous is null) + { + return; + } + + if (ReferenceEquals(previous, DisposedSentinel)) + { + value?.Dispose(); + return; + } + + throw new InvalidOperationException("Disposable already assigned."); + } + } + + /// + public void Dispose() + { + var previous = Interlocked.Exchange(ref _current, DisposedSentinel); + if (previous is null || ReferenceEquals(previous, DisposedSentinel)) + { + return; + } + + previous.Dispose(); + } +} diff --git a/src/ReactiveUI/Internal/Disposables/SwapDisposable.cs b/src/ReactiveUI/Internal/Disposables/SwapDisposable.cs new file mode 100644 index 0000000000..d882ee62b1 --- /dev/null +++ b/src/ReactiveUI/Internal/Disposables/SwapDisposable.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A disposable holder whose inner disposable can be re-assigned. The previous inner +/// disposable is disposed when replaced (in contrast to ). +/// Once this object is disposed, any subsequently assigned inner disposable is disposed +/// immediately. Replaces SerialDisposable. +/// +internal sealed class SwapDisposable : IDisposable +{ + /// The current inner disposable. + private IDisposable? _current; + + /// Disposed flag (0 = open, 1 = disposed), driven through . + private int _disposed; + + /// + /// Gets or sets the current inner disposable. Setting disposes the previous value. + /// + public IDisposable? Disposable + { + get => Volatile.Read(ref _current); + set => DisposableSlotHelper.SwapAndDisposePrevious(ref _current, ref _disposed, value); + } + + /// + public void Dispose() => DisposableSlotHelper.TryDispose(ref _current, ref _disposed); +} diff --git a/src/ReactiveUI/Internal/EventPatternObservable.cs b/src/ReactiveUI/Internal/EventPatternObservable.cs new file mode 100644 index 0000000000..5e8e713ed4 --- /dev/null +++ b/src/ReactiveUI/Internal/EventPatternObservable.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reflection; + +namespace ReactiveUI.Internal; + +/// +/// Bridges a reflection-resolved CLR event into an observable, emitting the event arguments on each raise. A tailored +/// replacement for Observable.FromEventPattern. +/// +/// The event arguments type. +/// The object exposing the event. +/// The name of the event to subscribe to. +[RequiresUnreferencedCode("Resolves the event and its handler delegate type by reflection, which may be trimmed.")] +internal sealed class EventPatternObservable(object target, string eventName) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + var eventInfo = target.GetType().GetRuntimeEvent(eventName) + ?? throw new InvalidOperationException($"Could not find event '{eventName}' on '{target.GetType()}'."); + var handlerType = eventInfo.EventHandlerType + ?? throw new InvalidOperationException($"Event '{eventName}' on '{target.GetType()}' has no handler type."); + + var forwarder = new Forwarder(observer); + var handler = forwarder.CreateHandler(handlerType); + eventInfo.AddEventHandler(target, handler); + + return new ActionDisposable(() => eventInfo.RemoveEventHandler(target, handler)); + } + + /// Forwards raised events to the observer, adapting to the event's handler delegate type. + /// The observer receiving the event arguments. + private sealed class Forwarder(IObserver observer) + { + /// Creates a delegate of the event's handler type that forwards to . + /// The event's handler delegate type. + /// A delegate compatible with the event. + public Delegate CreateHandler(Type handlerType) => + typeof(Forwarder).GetMethod(nameof(OnEvent), BindingFlags.Instance | BindingFlags.Public)!.CreateDelegate(handlerType, this); + + /// Forwards a raised event's arguments to the observer. + /// The event source. + /// The event arguments. + public void OnEvent(object? sender, TEventArgs args) => observer.OnNext(args); + } +} diff --git a/src/ReactiveUI/Internal/ExpressionChainParameters{TSender}.cs b/src/ReactiveUI/Internal/ExpressionChainParameters{TSender}.cs new file mode 100644 index 0000000000..e5e4c8d3e4 --- /dev/null +++ b/src/ReactiveUI/Internal/ExpressionChainParameters{TSender}.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; + +namespace ReactiveUI.Internal; + +/// +/// Bundles the configuration for an so the chain engine and its inner +/// sink can be constructed from a single value rather than a long parameter list. +/// +/// The root sender type surfaced on the emitted change. +/// The root object of the chain. +/// The full expression surfaced on the emitted change. +/// The member-access links of the chain, in order. +/// Whether to observe values before they change. +/// Whether to suppress POCO observation warnings. +/// When true, the first raw emission is suppressed. +/// When true, consecutive equal leaf values are suppressed. +/// Produces the change notifications for a link on a given parent value. +internal readonly record struct ExpressionChainParameters( + TSender? Source, + Expression? Expression, + Expression[] Links, + bool BeforeChange, + bool SuppressWarnings, + bool SkipInitial, + bool IsDistinct, + Func>> Notify); diff --git a/src/ReactiveUI/Internal/ExpressionChainSink.cs b/src/ReactiveUI/Internal/ExpressionChainSink.cs new file mode 100644 index 0000000000..bb7f3c889b --- /dev/null +++ b/src/ReactiveUI/Internal/ExpressionChainSink.cs @@ -0,0 +1,278 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; + +namespace ReactiveUI.Internal; + +/// +/// Walks an expression member chain (x.A.B.C) as a single switching engine: one watcher per link, each +/// observing its link on the value produced by the previous link and re-subscribing the deeper links when an +/// intermediate value changes. Emits the leaf value as an observed change, applying skip-initial (counted against the +/// raw stream), the non-null-parent filter, the cast to , and optional distinct-by-value +/// inline. Collapses the nested Select+Switch fold plus Skip/Where/Select/ +/// DistinctUntilChanged into one layer. +/// +/// The root sender type surfaced on the emitted change. +/// The leaf value type. +/// The configuration of the chain to observe. +[RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] +internal sealed class ExpressionChainSink(ExpressionChainParameters parameters) + : IObservable> +{ + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, parameters); + sink.Run(); + return sink; + } + + /// The running state of one chain subscription. + [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + private sealed class Sink : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes the chain mutations and emission. + private readonly Lock _gate = new(); +#else + /// Serializes the chain mutations and emission. + private readonly object _gate = new(); +#endif + + /// The observer receiving the leaf observed changes. + private readonly IObserver> _downstream; + + /// The root object of the chain. + private readonly TSender? _source; + + /// The full expression surfaced on the emitted change. + private readonly Expression? _expression; + + /// The member-access links of the chain, in order. + private readonly Expression[] _links; + + /// Whether values are observed before they change. + private readonly bool _beforeChange; + + /// Whether POCO observation warnings are suppressed. + private readonly bool _suppressWarnings; + + /// Whether consecutive equal leaf values are suppressed. + private readonly bool _isDistinct; + + /// Produces the change notifications for a link on a given parent value. + private readonly Func>> _notify; + + /// The per-link watchers. + private readonly Level[] _levels; + + /// Whether the next raw emission should be skipped (skip-initial). + private bool _skipNext; + + /// The last emitted leaf value, used by the distinct gate. + private TValue _last = default!; + + /// Whether holds a value yet. + private bool _hasLast; + + /// Latched once this chain subscription has been disposed. + private bool _disposed; + + /// Initializes a new instance of the class. + /// The observer receiving the leaf observed changes. + /// The configuration of the chain to observe. + public Sink( + IObserver> downstream, + ExpressionChainParameters parameters) + { + _downstream = downstream; + _source = parameters.Source; + _expression = parameters.Expression; + _links = parameters.Links; + _beforeChange = parameters.BeforeChange; + _suppressWarnings = parameters.SuppressWarnings; + _isDistinct = parameters.IsDistinct; + _notify = parameters.Notify; + _skipNext = parameters.SkipInitial; + _levels = new Level[_links.Length]; + for (var i = 0; i < _links.Length; i++) + { + _levels[i] = new(this, i, i == _links.Length - 1); + } + } + + /// Establishes the chain from the root value. + public void Run() + { + lock (_gate) + { + if (_links.Length == 0) + { + return; + } + + _levels[0].SetParent(_source); + } + } + + /// + public void Dispose() + { + lock (_gate) + { + _disposed = true; + for (var i = 0; i < _levels.Length; i++) + { + _levels[i].Dispose(); + } + } + } + + /// Sets the parent value of the level after . + /// The level index that produced the value. + /// The value the link produced (the parent for the next level). + private void SetNextParent(int level, object? value) => _levels[level + 1].SetParent(value); + + /// Handles a leaf raw emission: applies skip-initial, the non-null-parent filter, the cast and the distinct gate. + /// Whether the leaf's parent was null (a raw emission that the non-null filter drops). + /// The leaf value when the parent is present. + private void Emit(bool parentMissing, object? value) + { + if (_skipNext) + { + _skipNext = false; + return; + } + + if (parentMissing) + { + return; + } + + TValue typed; + if (value is null) + { + typed = default!; + } + else if (value is TValue cast) + { + typed = cast; + } + else + { + _downstream.OnError(new InvalidCastException($"Unable to cast from {value.GetType()} to {typeof(TValue)}.")); + return; + } + + if (_isDistinct && _hasLast && EqualityComparer.Default.Equals(typed, _last)) + { + return; + } + + _last = typed; + _hasLast = true; + _downstream.OnNext(new ObservedChange(_source!, _expression, typed)); + } + + /// A single chain link's watcher: re-subscribes on parent change and reads the link's value. + /// The owning chain sink. + /// This watcher's position in the chain. + /// Whether this is the final link in the chain. + [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + private sealed class Level(Sink sink, int index, bool isLeaf) : IDisposable + { + /// The current link-notification subscription; swapped on each re-parent. + private readonly SwapDisposable _subscription = new(); + + /// Re-establishes this watcher on a new parent value and propagates the current value downward. + /// The object this link is read from. + public void SetParent(object? parent) + { + if (parent is null) + { + _subscription.Disposable = null; + if (isLeaf) + { + sink.Emit(parentMissing: true, null); + } + else + { + sink.SetNextParent(index, null); + } + + return; + } + + var link = sink._links[index]; + + // Kicker: propagate the current value immediately, then subscribe for updates. + Push(ReadValue(parent, link)); + _subscription.Disposable = sink._notify(parent, link, sink._beforeChange, sink._suppressWarnings) + .Subscribe(new Observer(this)); + } + + /// + public void Dispose() => _subscription.Dispose(); + + /// Handles a notification for this link by re-reading the value and propagating it. + /// The notification (its value is read via reflection). + public void OnNotification(IObservedChange change) + { + lock (sink._gate) + { + if (sink._disposed) + { + return; + } + + Push(change.GetValueOrDefault()); + } + } + + /// Forwards a link-subscription error to the downstream observer. + /// The error to forward. + public void ForwardError(Exception error) => sink._downstream.OnError(error); + + /// Reads the current value of a link on a parent object via reflection. + /// The object the link is read from. + /// The member-access link. + /// The link's current value. + private static object? ReadValue(object parent, Expression link) => + new ObservedChange(parent, link, null).GetValueOrDefault(); + + /// Forwards this link's value to the next level, or emits it at the leaf. + /// The value this link produced. + private void Push(object? value) + { + if (isLeaf) + { + sink.Emit(parentMissing: false, value); + } + else + { + sink.SetNextParent(index, value); + } + } + + /// Forwards a link's notifications back into the level. + /// The owning level. + private sealed class Observer(Level level) : IObserver> + { + /// + public void OnNext(IObservedChange value) => level.OnNotification(value); + + /// + public void OnError(Exception error) => level.ForwardError(error); + + /// + public void OnCompleted() + { + } + } + } + } +} diff --git a/src/ReactiveUI/Internal/InteractionHandleObservable.cs b/src/ReactiveUI/Internal/InteractionHandleObservable.cs new file mode 100644 index 0000000000..982e54fe2f --- /dev/null +++ b/src/ReactiveUI/Internal/InteractionHandleObservable.cs @@ -0,0 +1,190 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive; +using System.Reactive.Concurrency; + +namespace ReactiveUI.Internal; + +/// +/// Runs an interaction's handlers in reverse registration order, one at a time on the handler scheduler, stopping as +/// soon as a handler marks the interaction handled; then emits the output or errors with an +/// . Collapses the +/// ToObservable+ObserveOn+Select(Defer)+Concat+TakeWhile+SelectMany+ +/// Concat(Defer(Return/Throw)) pipeline into a single sequential runner. +/// +/// The interaction's input type. +/// The interaction's output type. +/// The registered handlers in registration order (run from last to first). +/// The interaction context, used to read the handled flag and the output. +/// The scheduler each handler is invoked on. +/// The interaction, surfaced on the unhandled exception. +/// The interaction input, surfaced on the unhandled exception. +internal sealed class InteractionHandleObservable( + Func, IObservable>[] handlers, + IOutputContext context, + IScheduler scheduler, + Interaction interaction, + TInput input) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, handlers, context, scheduler, interaction, input); + sink.Start(); + return sink; + } + + /// Drives the sequential, scheduler-bound handler run for a single subscription. + private sealed class Sink : IDisposable + { + /// The observer receiving the interaction output. + private readonly IObserver _observer; + + /// The registered handlers in registration order. + private readonly Func, IObservable>[] _handlers; + + /// The interaction context (handled flag and output). + private readonly IOutputContext _context; + + /// The scheduler each handler is invoked on. + private readonly IScheduler _scheduler; + + /// The interaction, surfaced on the unhandled exception. + private readonly Interaction _interaction; + + /// The interaction input, surfaced on the unhandled exception. + private readonly TInput _input; + + /// Holds the current scheduled step or handler subscription; swapped as the run progresses. + private readonly SwapDisposable _current = new(); + + /// Latched once this run has been disposed. + private bool _disposed; + + /// Initializes a new instance of the class. + /// The observer receiving the interaction output. + /// The registered handlers in registration order. + /// The interaction context. + /// The scheduler each handler is invoked on. + /// The interaction, surfaced on the unhandled exception. + /// The interaction input, surfaced on the unhandled exception. + public Sink( + IObserver observer, + Func, IObservable>[] handlers, + IOutputContext context, + IScheduler scheduler, + Interaction interaction, + TInput input) + { + _observer = observer; + _handlers = handlers; + _context = context; + _scheduler = scheduler; + _interaction = interaction; + _input = input; + } + + /// Begins the run at the most-recently registered handler. + public void Start() => RunFrom(_handlers.Length - 1); + + /// + public void Dispose() + { + _disposed = true; + _current.Dispose(); + } + + /// Schedules the next handler step on the handler scheduler. + /// The handler index to run (descending toward the first-registered handler). + private void RunFrom(int index) => + _current.Disposable = _scheduler.Schedule( + (Self: this, Index: index), + static (_, state) => + { + state.Self.Step(state.Index); + return EmptyDisposable.Instance; + }); + + /// Runs the handler at , or finishes when handled or exhausted. + /// The handler index to run. + private void Step(int index) + { + if (_disposed) + { + return; + } + + if (_context.IsHandled || index < 0) + { + Finish(); + return; + } + + IObservable handlerResult; + try + { + handlerResult = _handlers[index](_context); + } + catch (Exception ex) + { + _observer.OnError(ex); + return; + } + + _current.Disposable = handlerResult.Subscribe(new HandlerObserver(this, index)); + } + + /// Emits the output once handled, or the unhandled-interaction error otherwise. + private void Finish() + { + if (_context.IsHandled) + { + TOutput output; + try + { + output = _context.GetOutput(); + } + catch (Exception ex) + { + _observer.OnError(ex); + return; + } + + _observer.OnNext(output); + _observer.OnCompleted(); + return; + } + + _observer.OnError(new UnhandledInteractionException(_interaction, _input)); + } + + /// Advances to the next-older handler once the current one completes. + /// The index of the handler that just completed. + private void OnHandlerCompleted(int index) => RunFrom(index - 1); + + /// Forwards a handler error to the observer. + /// The error to forward. + private void OnHandlerError(Exception error) => _observer.OnError(error); + + /// Observes a single handler's completion, ignoring its values. + /// The owning run. + /// The index of the handler being observed. + private sealed class HandlerObserver(Sink sink, int index) : IObserver + { + /// + public void OnNext(Unit value) + { + } + + /// + public void OnError(Exception error) => sink.OnHandlerError(error); + + /// + public void OnCompleted() => sink.OnHandlerCompleted(index); + } + } +} diff --git a/src/ReactiveUI/Internal/NeverObservable.cs b/src/ReactiveUI/Internal/NeverObservable.cs new file mode 100644 index 0000000000..eacd42557f --- /dev/null +++ b/src/ReactiveUI/Internal/NeverObservable.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// An observable that never produces any notification (no value, completion, or error). Replaces Observable.Never. +/// +/// The element type the observable would have produced. +internal sealed class NeverObservable : IObservable +{ + /// The shared singleton instance. + public static readonly NeverObservable Instance = new(); + + /// Prevents a default instance of the class from being created externally. + private NeverObservable() + { + } + + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return EmptyDisposable.Instance; + } +} diff --git a/src/ReactiveUI/Internal/ObservableForPropertySink.cs b/src/ReactiveUI/Internal/ObservableForPropertySink.cs new file mode 100644 index 0000000000..2c1091e182 --- /dev/null +++ b/src/ReactiveUI/Internal/ObservableForPropertySink.cs @@ -0,0 +1,138 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; + +namespace ReactiveUI.Internal; + +/// +/// Single-layer observable for a single property: optionally emits the current value on subscribe, then re-reads and +/// emits the property value on each notification from the underlying change source, optionally suppressing duplicate +/// values. Replaces an Observable.Create + DistinctUntilChanged pair. +/// +/// The type of the observed object. +/// The property value type. +/// The observed object. +/// The expression surfaced on the observed change. +/// The name of the observed property. +/// The underlying change-notification source (a tick re-reads the value). +/// Reads the current property value from the sender. +/// When true, the current value is not emitted on subscribe. +/// When true, consecutive equal values are suppressed. +internal sealed class ObservableForPropertySink( + TSender sender, + Expression expression, + string propertyName, + IObservable> notifications, + Func getValue, + bool skipInitial, + bool isDistinct) : IObservable> +{ + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, sender, expression, propertyName, getValue, isDistinct); + return sink.Run(notifications, skipInitial); + } + + /// Reads the property value on each notification and forwards it as an observed change. + private sealed class Sink : IObserver> + { + /// The observer receiving observed changes. + private readonly IObserver> _downstream; + + /// The observed object. + private readonly TSender _sender; + + /// The expression surfaced on the observed change. + private readonly Expression _expression; + + /// The name of the observed property. + private readonly string _propertyName; + + /// Reads the current property value from the sender. + private readonly Func _getValue; + + /// Whether consecutive equal values are suppressed. + private readonly bool _isDistinct; + + /// The last emitted value, used by the distinct gate. + private TValue _last = default!; + + /// Whether holds a value yet. + private bool _hasLast; + + /// Initializes a new instance of the class. + /// The observer receiving observed changes. + /// The observed object. + /// The expression surfaced on the observed change. + /// The name of the observed property. + /// Reads the current property value. + /// Whether consecutive equal values are suppressed. + public Sink( + IObserver> downstream, + TSender sender, + Expression expression, + string propertyName, + Func getValue, + bool isDistinct) + { + _downstream = downstream; + _sender = sender; + _expression = expression; + _propertyName = propertyName; + _getValue = getValue; + _isDistinct = isDistinct; + } + + /// Optionally emits the current value, then subscribes to the notification source. + /// The change-notification source. + /// When true, the current value is not emitted on subscribe. + /// The notification-source subscription. + public IDisposable Run(IObservable> notifications, bool skipInitial) + { + if (!skipInitial) + { + Emit(); + } + + return notifications.Subscribe(this); + } + + /// + public void OnNext(IObservedChange value) => Emit(); + + /// + public void OnError(Exception error) => _downstream.OnError(error); + + /// + public void OnCompleted() => _downstream.OnCompleted(); + + /// Reads the current property value and forwards it as an observed change, honoring the distinct gate. + private void Emit() + { + TValue current; + try + { + current = _getValue(_sender, _propertyName); + } + catch (Exception ex) + { + _downstream.OnError(ex); + return; + } + + if (_isDistinct && _hasLast && EqualityComparer.Default.Equals(current, _last)) + { + return; + } + + _last = current; + _hasLast = true; + _downstream.OnNext(new ObservedChange(_sender, _expression, current)); + } + } +} diff --git a/src/ReactiveUI/Internal/ObservedChangeValueSelector.cs b/src/ReactiveUI/Internal/ObservedChangeValueSelector.cs new file mode 100644 index 0000000000..ceafde8caa --- /dev/null +++ b/src/ReactiveUI/Internal/ObservedChangeValueSelector.cs @@ -0,0 +1,56 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Projects the value of each observed change through a selector. Backs the ObservableForProperty overloads +/// that take a value selector, replacing a .Select(x => selector(x.Value)) hop. +/// +/// The observed-change sender type. +/// The observed-change value type. +/// The projected result type. +/// The source observed-change stream. +/// Projects an observed change's value into the result. +internal sealed class ObservedChangeValueSelector( + IObservable> source, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } + + /// Applies the selector to each observed change's value and forwards the result. + /// The observer receiving projected results. + /// Projects an observed change's value into the result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver> + { + /// + public void OnNext(IObservedChange value) + { + TRet result; + try + { + result = selector(value.Value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } +} diff --git a/src/ReactiveUI/Internal/SingleValueObservable.cs b/src/ReactiveUI/Internal/SingleValueObservable.cs new file mode 100644 index 0000000000..792a44918f --- /dev/null +++ b/src/ReactiveUI/Internal/SingleValueObservable.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive; + +namespace ReactiveUI.Internal; + +/// +/// Provides pre-allocated singletons for values emitted often enough to be +/// worth sharing rather than re-allocating on each use. +/// +internal static class SingleValueObservable +{ + /// + /// A shared cold observable that emits once and then completes. Reused to avoid the + /// allocation of a fresh for the very common "completed unit" case. + /// + public static readonly IObservable Unit = new SingleValueObservable(System.Reactive.Unit.Default); + + /// A shared cold observable that emits a single true and then completes. + public static readonly IObservable True = new SingleValueObservable(true); + + /// A shared cold observable that emits a single false and then completes. + public static readonly IObservable False = new SingleValueObservable(false); +} diff --git a/src/ReactiveUI/Internal/SingleValueObservable{T}.cs b/src/ReactiveUI/Internal/SingleValueObservable{T}.cs new file mode 100644 index 0000000000..0becdb3620 --- /dev/null +++ b/src/ReactiveUI/Internal/SingleValueObservable{T}.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A cold observable that emits a single value and then completes on each subscription. +/// Replaces Observable.Return for internal use. +/// +/// The element type. +/// The value emitted to each subscriber. +internal sealed class SingleValueObservable(T value) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + observer.OnNext(value); + observer.OnCompleted(); + return EmptyDisposable.Instance; + } +} diff --git a/src/ReactiveUI/Internal/StartObservable.cs b/src/ReactiveUI/Internal/StartObservable.cs new file mode 100644 index 0000000000..85700afdc1 --- /dev/null +++ b/src/ReactiveUI/Internal/StartObservable.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Concurrency; + +namespace ReactiveUI.Internal; + +/// +/// Runs a function on a scheduler when subscribed, then emits its result and completes. +/// Replaces Observable.Start for internal use. +/// +/// The result type. +/// The function to run on the scheduler. +/// The scheduler the function runs on. +internal sealed class StartObservable(Func function, IScheduler scheduler) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return scheduler.Schedule( + (Function: function, Observer: observer), + static (_, state) => + { + try + { + var result = state.Function(); + state.Observer.OnNext(result); + state.Observer.OnCompleted(); + } + catch (Exception ex) + { + state.Observer.OnError(ex); + } + + return EmptyDisposable.Instance; + }); + } +} diff --git a/src/ReactiveUI/Internal/Subjects/BehaviorBroadcastSubject.cs b/src/ReactiveUI/Internal/Subjects/BehaviorBroadcastSubject.cs new file mode 100644 index 0000000000..70e0d18a18 --- /dev/null +++ b/src/ReactiveUI/Internal/Subjects/BehaviorBroadcastSubject.cs @@ -0,0 +1,135 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A subject that remembers its latest value and replays it to each new subscriber, replacing +/// System.Reactive.Subjects.BehaviorSubject<T>. +/// +/// The element type. +/// The value replayed until the first . +internal sealed class BehaviorBroadcastSubject(T initialValue) : IReactiveSubject, IDisposable +{ + /// Guards the latest value, observer set, and terminal state. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observers subscribed to this subject. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _broadcaster; + + /// The latest value, replayed to new subscribers. + private T _value = initialValue; + + /// The terminal error, if the subject errored. + private Exception? _error; + + /// Whether the subject has terminated (completed or errored). + private bool _stopped; + + /// + public void OnNext(T value) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _value = value; + } + + _broadcaster.Next(value); + } + + /// + public void OnError(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + _error = error; + } + + _broadcaster.Error(error); + } + + /// + public void OnCompleted() + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _broadcaster.Completed(); + } + + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + T current; + lock (_gate) + { + if (_error is not null) + { + observer.OnError(_error); + return EmptyDisposable.Instance; + } + + if (_stopped) + { + observer.OnCompleted(); + return EmptyDisposable.Instance; + } + + _broadcaster.Add(observer); + current = _value; + } + + observer.OnNext(current); + return new Subscription(this, observer); + } + + /// + public void Dispose() + { + } + + /// Removes an observer from the subject. + /// The observer to remove. + private void Unsubscribe(IObserver observer) + { + lock (_gate) + { + _broadcaster.Remove(observer); + } + } + + /// Removes its observer from the subject when disposed. + /// The owning subject. + /// The subscribed observer. + private sealed class Subscription(BehaviorBroadcastSubject parent, IObserver observer) : IDisposable + { + /// + public void Dispose() => parent.Unsubscribe(observer); + } +} diff --git a/src/ReactiveUI/Internal/Subjects/BroadcastSubject.cs b/src/ReactiveUI/Internal/Subjects/BroadcastSubject.cs new file mode 100644 index 0000000000..fc7ac38907 --- /dev/null +++ b/src/ReactiveUI/Internal/Subjects/BroadcastSubject.cs @@ -0,0 +1,135 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Subjects; + +namespace ReactiveUI.Internal; + +/// +/// A lean multicast subject backed by , replacing System.Reactive.Subjects.Subject<T>. +/// Late subscribers receive only values produced after they subscribe; once terminated it replays the terminal state. +/// +/// The element type. +internal sealed class BroadcastSubject : IReactiveSubject, ISubject, IDisposable +{ + /// Guards the observer set and terminal state. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observers subscribed to this subject. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _broadcaster; + + /// The terminal error, if the subject errored. + private Exception? _error; + + /// Whether the subject has terminated (completed or errored). + private bool _stopped; + + /// Whether the subject has been disposed. + private bool _disposed; + + /// + public void OnNext(T value) + { + lock (_gate) + { + if (_stopped || _disposed) + { + return; + } + } + + _broadcaster.Next(value); + } + + /// + public void OnError(Exception error) + { + lock (_gate) + { + if (_stopped || _disposed) + { + return; + } + + _stopped = true; + _error = error; + } + + _broadcaster.Error(error); + } + + /// + public void OnCompleted() + { + lock (_gate) + { + if (_stopped || _disposed) + { + return; + } + + _stopped = true; + } + + _broadcaster.Completed(); + } + + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + lock (_gate) + { + if (_error is not null) + { + observer.OnError(_error); + return EmptyDisposable.Instance; + } + + if (_stopped) + { + observer.OnCompleted(); + return EmptyDisposable.Instance; + } + + _broadcaster.Add(observer); + } + + return new Subscription(this, observer); + } + + /// + public void Dispose() + { + lock (_gate) + { + _disposed = true; + } + } + + /// Removes an observer from the subject. + /// The observer to remove. + private void Unsubscribe(IObserver observer) + { + lock (_gate) + { + _broadcaster.Remove(observer); + } + } + + /// Removes its observer from the subject when disposed. + /// The owning subject. + /// The subscribed observer. + private sealed class Subscription(BroadcastSubject parent, IObserver observer) : IDisposable + { + /// + public void Dispose() => parent.Unsubscribe(observer); + } +} diff --git a/src/ReactiveUI/Internal/Subjects/DelayableNotificationSubject.cs b/src/ReactiveUI/Internal/Subjects/DelayableNotificationSubject.cs new file mode 100644 index 0000000000..f98d097611 --- /dev/null +++ b/src/ReactiveUI/Internal/Subjects/DelayableNotificationSubject.cs @@ -0,0 +1,162 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A subject that passes notifications through immediately while change notifications are not delayed, but buffers +/// them while delayed and emits a de-duplicated batch when is called (on the delay window opening +/// or closing). Fuses the Buffer(boundary).SelectMany(distinct).Publish().RefCount() pipeline that ReactiveUI +/// used for delayable property-change notifications into one allocation-light sink. +/// +/// The notification type. +/// Returns whether change notifications are currently delayed. +/// De-duplicates a buffered batch before it is emitted on flush. +internal sealed class DelayableNotificationSubject( + Func isDelayed, + Func, IEnumerable> flushDistinct) : IReactiveSubject, IDisposable +{ + /// Guards the observer set, buffer, and terminal state. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observers subscribed to this subject. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _broadcaster; + + /// Holds notifications produced while delayed; null until the first buffered notification. + private List? _buffer; + + /// The terminal error, if the subject errored. + private Exception? _error; + + /// Whether the subject has terminated (completed or errored). + private bool _stopped; + + /// + public void OnNext(T value) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + if (isDelayed()) + { + (_buffer ??= []).Add(value); + return; + } + } + + _broadcaster.Next(value); + } + + /// + public void OnError(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + _error = error; + } + + _broadcaster.Error(error); + } + + /// + public void OnCompleted() + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _broadcaster.Completed(); + } + + /// Emits any buffered notifications as a de-duplicated batch; called when the delay window opens or closes. + public void Flush() + { + List batch; + lock (_gate) + { + if (_stopped || _buffer is null || _buffer.Count == 0) + { + return; + } + + batch = [.. flushDistinct(_buffer)]; + _buffer.Clear(); + } + + for (var i = 0; i < batch.Count; i++) + { + _broadcaster.Next(batch[i]); + } + } + + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + lock (_gate) + { + if (_error is not null) + { + observer.OnError(_error); + return EmptyDisposable.Instance; + } + + if (_stopped) + { + observer.OnCompleted(); + return EmptyDisposable.Instance; + } + + _broadcaster.Add(observer); + } + + return new Subscription(this, observer); + } + + /// + public void Dispose() + { + } + + /// Removes an observer from the subject. + /// The observer to remove. + private void Unsubscribe(IObserver observer) + { + lock (_gate) + { + _broadcaster.Remove(observer); + } + } + + /// Removes its observer from the subject when disposed. + /// The owning subject. + /// The subscribed observer. + private sealed class Subscription(DelayableNotificationSubject parent, IObserver observer) : IDisposable + { + /// + public void Dispose() => parent.Unsubscribe(observer); + } +} diff --git a/src/ReactiveUI/Internal/Subjects/ReplayBroadcastSubject.cs b/src/ReactiveUI/Internal/Subjects/ReplayBroadcastSubject.cs new file mode 100644 index 0000000000..db7952da51 --- /dev/null +++ b/src/ReactiveUI/Internal/Subjects/ReplayBroadcastSubject.cs @@ -0,0 +1,145 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// A subject that remembers the last bufferSize values and replays them to each new subscriber, replacing +/// System.Reactive.Subjects.ReplaySubject<T> with a bounded buffer. +/// +/// The element type. +/// The maximum number of recent values replayed to new subscribers. +internal sealed class ReplayBroadcastSubject(int bufferSize) : IReactiveSubject, IDisposable +{ + /// Guards the buffer, observer set, and terminal state. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The most recent values, capped at the buffer size, replayed to new subscribers. + private readonly List _buffer = new(bufferSize); + + /// The observers subscribed to this subject. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _broadcaster; + + /// The terminal error, if the subject errored. + private Exception? _error; + + /// Whether the subject has terminated (completed or errored). + private bool _stopped; + + /// + public void OnNext(T value) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _buffer.Add(value); + if (_buffer.Count > bufferSize) + { + _buffer.RemoveAt(0); + } + } + + _broadcaster.Next(value); + } + + /// + public void OnError(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + _error = error; + } + + _broadcaster.Error(error); + } + + /// + public void OnCompleted() + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _broadcaster.Completed(); + } + + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + lock (_gate) + { + for (var i = 0; i < _buffer.Count; i++) + { + observer.OnNext(_buffer[i]); + } + + if (_error is not null) + { + observer.OnError(_error); + return EmptyDisposable.Instance; + } + + if (_stopped) + { + observer.OnCompleted(); + return EmptyDisposable.Instance; + } + + _broadcaster.Add(observer); + } + + return new Subscription(this, observer); + } + + /// + public void Dispose() + { + lock (_gate) + { + _buffer.Clear(); + } + } + + /// Removes an observer from the subject. + /// The observer to remove. + private void Unsubscribe(IObserver observer) + { + lock (_gate) + { + _broadcaster.Remove(observer); + } + } + + /// Removes its observer from the subject when disposed. + /// The owning subject. + /// The subscribed observer. + private sealed class Subscription(ReplayBroadcastSubject parent, IObserver observer) : IDisposable + { + /// + public void Dispose() => parent.Unsubscribe(observer); + } +} diff --git a/src/ReactiveUI/Internal/Subjects/SchedulingObserver.cs b/src/ReactiveUI/Internal/Subjects/SchedulingObserver.cs new file mode 100644 index 0000000000..dc5e0b06a2 --- /dev/null +++ b/src/ReactiveUI/Internal/Subjects/SchedulingObserver.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Concurrency; + +namespace ReactiveUI.Internal; + +/// +/// Forwards each notification to a downstream observer on a scheduler, replacing a per-observer ObserveOn. +/// +/// The element type. +/// The observer that receives the scheduled notifications. +/// The scheduler each notification is delivered on. +internal sealed class SchedulingObserver(IObserver downstream, IScheduler scheduler) : IObserver +{ + /// + public void OnNext(T value) => + scheduler.Schedule( + (Observer: downstream, Value: value), + static (_, state) => + { + state.Observer.OnNext(state.Value); + return EmptyDisposable.Instance; + }); + + /// + public void OnError(Exception error) => + scheduler.Schedule( + (Observer: downstream, Error: error), + static (_, state) => + { + state.Observer.OnError(state.Error); + return EmptyDisposable.Instance; + }); + + /// + public void OnCompleted() => + scheduler.Schedule( + downstream, + static (_, observer) => + { + observer.OnCompleted(); + return EmptyDisposable.Instance; + }); +} diff --git a/src/ReactiveUI/Internal/SyncExecuteObservable.cs b/src/ReactiveUI/Internal/SyncExecuteObservable.cs new file mode 100644 index 0000000000..21608c0616 --- /dev/null +++ b/src/ReactiveUI/Internal/SyncExecuteObservable.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Runs a function synchronously on each subscription, then emits its result and completes. +/// A fault in the function is forwarded as an error. Backs the synchronous command factories. +/// +/// The result type. +/// The function to run on subscription. +internal sealed class SyncExecuteObservable(Func execute) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + try + { + var result = execute(); + observer.OnNext(result); + observer.OnCompleted(); + } + catch (Exception ex) + { + observer.OnError(ex); + } + + return EmptyDisposable.Instance; + } +} diff --git a/src/ReactiveUI/Internal/TaskObservable.cs b/src/ReactiveUI/Internal/TaskObservable.cs new file mode 100644 index 0000000000..fad7e46fba --- /dev/null +++ b/src/ReactiveUI/Internal/TaskObservable.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Bridges a to an observable: emits the task's result and completes, +/// or forwards its fault / cancellation as an error. Replaces task.ToObservable() for internal use. +/// +/// The task's result type. +/// The task to observe. +internal sealed class TaskObservable(Task task) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + task.ContinueWith( + static (completed, state) => + { + var observer = (IObserver)state!; + if (completed.IsFaulted) + { + observer.OnError(completed.Exception?.InnerException ?? completed.Exception!); + } + else if (completed.IsCanceled) + { + observer.OnError(new TaskCanceledException(completed)); + } + else + { + observer.OnNext(completed.Result); + observer.OnCompleted(); + } + }, + observer, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + return EmptyDisposable.Instance; + } +} diff --git a/src/ReactiveUI/Internal/TaskUnitObservable.cs b/src/ReactiveUI/Internal/TaskUnitObservable.cs new file mode 100644 index 0000000000..e5205187d1 --- /dev/null +++ b/src/ReactiveUI/Internal/TaskUnitObservable.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive; + +namespace ReactiveUI.Internal; + +/// +/// Bridges a non-generic to an observable: emits and +/// completes when the task completes, or forwards its fault / cancellation as an error. Replaces +/// task.ToObservable() for internal use. +/// +/// The task to observe. +internal sealed class TaskUnitObservable(Task task) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + task.ContinueWith( + static (completed, state) => + { + var observer = (IObserver)state!; + if (completed.IsFaulted) + { + observer.OnError(completed.Exception?.InnerException ?? completed.Exception!); + } + else if (completed.IsCanceled) + { + observer.OnError(new TaskCanceledException(completed)); + } + else + { + observer.OnNext(Unit.Default); + observer.OnCompleted(); + } + }, + observer, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + return EmptyDisposable.Instance; + } +} diff --git a/src/ReactiveUI/Internal/ThrowObservable.cs b/src/ReactiveUI/Internal/ThrowObservable.cs new file mode 100644 index 0000000000..06426956c3 --- /dev/null +++ b/src/ReactiveUI/Internal/ThrowObservable.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// An observable that delivers a single error to every subscriber on subscription. Replaces Observable.Throw. +/// +/// The (never-produced) element type. +/// The error delivered to subscribers. +internal sealed class ThrowObservable(Exception error) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + observer.OnError(error); + return EmptyDisposable.Instance; + } +} diff --git a/src/ReactiveUI/Internal/ToUnitObservable.cs b/src/ReactiveUI/Internal/ToUnitObservable.cs new file mode 100644 index 0000000000..f2c59b0134 --- /dev/null +++ b/src/ReactiveUI/Internal/ToUnitObservable.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive; + +namespace ReactiveUI.Internal; + +/// +/// Maps each value of a source observable to , preserving completion and error. +/// Replaces a .Select(_ => Unit.Default) hop used to erase a handler's element type. +/// +/// The source element type. +/// The source observable whose values are erased to . +internal sealed class ToUnitObservable(IObservable source) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// Forwards a unit per source value and relays completion and error. + /// The observer receiving unit values. + private sealed class Sink(IObserver downstream) : IObserver + { + /// + public void OnNext(T value) => downstream.OnNext(Unit.Default); + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/EmptyObservable.cs b/src/ReactiveUI/Internal/WhenAny/EmptyObservable.cs new file mode 100644 index 0000000000..570325bd8d --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/EmptyObservable.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// An observable that completes immediately on subscription without producing a value. +/// Used by the WhenAnyObservable mixins to stand in for a null inner observable. +/// +/// The element type the observable would have produced. +internal sealed class EmptyObservable : IObservable +{ + /// + /// The shared singleton instance. + /// + public static readonly EmptyObservable Instance = new(); + + /// + /// Initializes a new instance of the class. + /// + private EmptyObservable() + { + } + + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + observer.OnCompleted(); + return EmptyDisposable.Instance; + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity1.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity1.cs new file mode 100644 index 0000000000..f5e421990f --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity1.cs @@ -0,0 +1,57 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Projects a single source through a selector. The fused arity-1 form of the +/// WhenAny combine sink: no gate is needed for one source. +/// +/// The input type of source 1. +/// The type produced by the selector. +/// The source observable. +/// Projects the source element into the result. +internal sealed class WhenAnyChangeSink( + IObservable source, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } + + /// + /// Projects each source notification through the selector and forwards the result. + /// + /// The observer receiving the projected results. + /// Projects the source element into the result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver + { + /// + public void OnNext(T1 change) + { + TResult result; + try + { + result = selector(change); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity10.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity10.cs new file mode 100644 index 0000000000..5d1de76b55 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity10.cs @@ -0,0 +1,396 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 10 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The input type of source 6. +/// The input type of source 7. +/// The input type of source 8. +/// The input type of source 9. +/// The input type of source 10. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Source observable 10. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9, + IObservable source10, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9, source10); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[10]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// The latest value captured from source 10. + private T10 _value10 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// Whether source 10 has produced a value yet. + private bool _has10; + + /// The number of sources that have not yet completed. + private int _active = 10; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + /// Source observable 10. + public void Run( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9, + IObservable source10) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver(On9, OnError, OnSourceCompleted)); + _subscriptions[i++] = source10.Subscribe(new DelegateObserver(On10, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(T6 change) + { + lock (_gate) + { + _value6 = change; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(T7 change) + { + lock (_gate) + { + _value7 = change; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(T8 change) + { + lock (_gate) + { + _value8 = change; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(T9 change) + { + lock (_gate) + { + _value9 = change; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 10 and emits when every source is ready. + /// The notification from source 10. + public void On10(T10 change) + { + lock (_gate) + { + _value10 = change; + _has10 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9, _value10); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity11.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity11.cs new file mode 100644 index 0000000000..1713075a9a --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity11.cs @@ -0,0 +1,426 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 11 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The input type of source 6. +/// The input type of source 7. +/// The input type of source 8. +/// The input type of source 9. +/// The input type of source 10. +/// The input type of source 11. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Source observable 10. +/// Source observable 11. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9, + IObservable source10, + IObservable source11, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9, source10, source11); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + [SuppressMessage("Major Code Smell", "S1541:Methods and properties should not be too complex", Justification = "Cyclomatic complexity is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[11]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// The latest value captured from source 10. + private T10 _value10 = default!; + + /// The latest value captured from source 11. + private T11 _value11 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// Whether source 10 has produced a value yet. + private bool _has10; + + /// Whether source 11 has produced a value yet. + private bool _has11; + + /// The number of sources that have not yet completed. + private int _active = 11; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + /// Source observable 10. + /// Source observable 11. + public void Run( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9, + IObservable source10, + IObservable source11) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver(On9, OnError, OnSourceCompleted)); + _subscriptions[i++] = source10.Subscribe(new DelegateObserver(On10, OnError, OnSourceCompleted)); + _subscriptions[i++] = source11.Subscribe(new DelegateObserver(On11, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(T6 change) + { + lock (_gate) + { + _value6 = change; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(T7 change) + { + lock (_gate) + { + _value7 = change; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(T8 change) + { + lock (_gate) + { + _value8 = change; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(T9 change) + { + lock (_gate) + { + _value9 = change; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 10 and emits when every source is ready. + /// The notification from source 10. + public void On10(T10 change) + { + lock (_gate) + { + _value10 = change; + _has10 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 11 and emits when every source is ready. + /// The notification from source 11. + public void On11(T11 change) + { + lock (_gate) + { + _value11 = change; + _has11 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9, _value10, _value11); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity12.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity12.cs new file mode 100644 index 0000000000..bdf27b5575 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity12.cs @@ -0,0 +1,455 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 12 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The input type of source 6. +/// The input type of source 7. +/// The input type of source 8. +/// The input type of source 9. +/// The input type of source 10. +/// The input type of source 11. +/// The input type of source 12. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Source observable 10. +/// Source observable 11. +/// Source observable 12. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9, + IObservable source10, + IObservable source11, + IObservable source12, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9, source10, source11, source12); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + [SuppressMessage("Major Code Smell", "S1541:Methods and properties should not be too complex", Justification = "Cyclomatic complexity is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[12]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// The latest value captured from source 10. + private T10 _value10 = default!; + + /// The latest value captured from source 11. + private T11 _value11 = default!; + + /// The latest value captured from source 12. + private T12 _value12 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// Whether source 10 has produced a value yet. + private bool _has10; + + /// Whether source 11 has produced a value yet. + private bool _has11; + + /// Whether source 12 has produced a value yet. + private bool _has12; + + /// The number of sources that have not yet completed. + private int _active = 12; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + /// Source observable 10. + /// Source observable 11. + /// Source observable 12. + public void Run( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9, + IObservable source10, + IObservable source11, + IObservable source12) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver(On9, OnError, OnSourceCompleted)); + _subscriptions[i++] = source10.Subscribe(new DelegateObserver(On10, OnError, OnSourceCompleted)); + _subscriptions[i++] = source11.Subscribe(new DelegateObserver(On11, OnError, OnSourceCompleted)); + _subscriptions[i++] = source12.Subscribe(new DelegateObserver(On12, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(T6 change) + { + lock (_gate) + { + _value6 = change; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(T7 change) + { + lock (_gate) + { + _value7 = change; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(T8 change) + { + lock (_gate) + { + _value8 = change; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(T9 change) + { + lock (_gate) + { + _value9 = change; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 10 and emits when every source is ready. + /// The notification from source 10. + public void On10(T10 change) + { + lock (_gate) + { + _value10 = change; + _has10 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 11 and emits when every source is ready. + /// The notification from source 11. + public void On11(T11 change) + { + lock (_gate) + { + _value11 = change; + _has11 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 12 and emits when every source is ready. + /// The notification from source 12. + public void On12(T12 change) + { + lock (_gate) + { + _value12 = change; + _has12 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9, _value10, _value11, _value12); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity2.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity2.cs new file mode 100644 index 0000000000..cbd2289811 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity2.cs @@ -0,0 +1,160 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 2 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Combines the ready values into a result. +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[2]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// The number of sources that have not yet completed. + private int _active = 2; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + public void Run(IObservable source1, IObservable source2) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!_has2) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!_has1) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity3.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity3.cs new file mode 100644 index 0000000000..ea7c861b9e --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity3.cs @@ -0,0 +1,188 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 3 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Combines the ready values into a result. +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[3]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// The number of sources that have not yet completed. + private int _active = 3; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + public void Run(IObservable source1, IObservable source2, IObservable source3) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity4.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity4.cs new file mode 100644 index 0000000000..54a1244b07 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity4.cs @@ -0,0 +1,216 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 4 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Combines the ready values into a result. +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[4]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// The number of sources that have not yet completed. + private int _active = 4; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + public void Run(IObservable source1, IObservable source2, IObservable source3, IObservable source4) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity5.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity5.cs new file mode 100644 index 0000000000..823045c09a --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity5.cs @@ -0,0 +1,244 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 5 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Combines the ready values into a result. +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[5]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// The number of sources that have not yet completed. + private int _active = 5; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + public void Run(IObservable source1, IObservable source2, IObservable source3, IObservable source4, IObservable source5) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity6.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity6.cs new file mode 100644 index 0000000000..2e371588c4 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity6.cs @@ -0,0 +1,272 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 6 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The input type of source 6. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Combines the ready values into a result. +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[6]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// The number of sources that have not yet completed. + private int _active = 6; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + public void Run(IObservable source1, IObservable source2, IObservable source3, IObservable source4, IObservable source5, IObservable source6) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver(On6, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(T6 change) + { + lock (_gate) + { + _value6 = change; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity7.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity7.cs new file mode 100644 index 0000000000..eec07b9744 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity7.cs @@ -0,0 +1,308 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 7 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The input type of source 6. +/// The input type of source 7. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[7]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// The number of sources that have not yet completed. + private int _active = 7; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + public void Run( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver(On7, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(T6 change) + { + lock (_gate) + { + _value6 = change; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(T7 change) + { + lock (_gate) + { + _value7 = change; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity8.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity8.cs new file mode 100644 index 0000000000..b38544d61a --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity8.cs @@ -0,0 +1,338 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 8 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The input type of source 6. +/// The input type of source 7. +/// The input type of source 8. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[8]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// The number of sources that have not yet completed. + private int _active = 8; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + public void Run( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver(On8, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(T6 change) + { + lock (_gate) + { + _value6 = change; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(T7 change) + { + lock (_gate) + { + _value7 = change; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(T8 change) + { + lock (_gate) + { + _value8 = change; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity9.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity9.cs new file mode 100644 index 0000000000..4ee7788417 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyChangeSink.Arity9.cs @@ -0,0 +1,367 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines the latest of 9 sources, emitting the selector result once every source +/// has produced a value. Bails on a plain if when peers are not ready. +/// +/// The input type of source 1. +/// The input type of source 2. +/// The input type of source 3. +/// The input type of source 4. +/// The input type of source 5. +/// The input type of source 6. +/// The input type of source 7. +/// The input type of source 8. +/// The input type of source 9. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyChangeSink( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[9]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// The number of sources that have not yet completed. + private int _active = 9; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + public void Run( + IObservable source1, + IObservable source2, + IObservable source3, + IObservable source4, + IObservable source5, + IObservable source6, + IObservable source7, + IObservable source8, + IObservable source9) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver(On9, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(T1 change) + { + lock (_gate) + { + _value1 = change; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(T2 change) + { + lock (_gate) + { + _value2 = change; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(T3 change) + { + lock (_gate) + { + _value3 = change; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(T4 change) + { + lock (_gate) + { + _value4 = change; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(T5 change) + { + lock (_gate) + { + _value5 = change; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(T6 change) + { + lock (_gate) + { + _value6 = change; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(T7 change) + { + lock (_gate) + { + _value7 = change; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(T8 change) + { + lock (_gate) + { + _value8 = change; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(T9 change) + { + lock (_gate) + { + _value9 = change; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyObservableMergeSink.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyObservableMergeSink.cs new file mode 100644 index 0000000000..44493e1f6a --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyObservableMergeSink.cs @@ -0,0 +1,167 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// The arity-N (no-selector) WhenAnyObservable sink: each time the observed properties +/// produce a new set of inner observables, it unsubscribes from the previous set and +/// merges the new set, forwarding every value from every current inner. The switch and +/// merge logic is written inline; there is no operator call. +/// +/// The element type produced by the inner observables. +/// The outer observable; each emission is the current set of inner observables. +internal sealed class WhenAnyObservableMergeSink(IObservable[]> sources) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer); + sink.Run(sources); + return sink; + } + + /// + /// Tracks the active set of inner subscriptions and forwards values from all of them. + /// + /// The observer receiving the merged values. + private sealed class Sink(IObserver downstream) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes outer and inner notifications. + private readonly Lock _gate = new(); +#else + /// Serializes outer and inner notifications. + private readonly object _gate = new(); +#endif + + /// Holds the current generation's inner subscriptions; disposed when a new set arrives. + private readonly SwapDisposable _innerGroup = new(); + + /// The subscription to the outer observable. + private IDisposable? _outer; + + /// Monotonic id of the current inner set; notifications from older sets are ignored. + private int _index; + + /// The number of current-generation inners that have not yet completed. + private int _activeInners; + + /// Whether the current generation has any active inners. + private bool _hasCurrent; + + /// Whether the outer observable has completed. + private bool _outerDone; + + /// Subscribes to the outer observable. + /// The outer observable of inner-observable sets. + public void Run(IObservable[]> source) => + _outer = source.Subscribe(new DelegateObserver[]>(OnNextOuter, OnError, OnOuterCompleted)); + + /// Forwards an error from the outer or any current inner and tears down. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// + public void Dispose() + { + _outer?.Dispose(); + _innerGroup.Dispose(); + } + + /// Switches to a new set of inner observables, dropping the previous set, and merges it. + /// The new set of inner observables. + private void OnNextOuter(IObservable[] inners) + { + int id; + lock (_gate) + { + id = ++_index; + _activeInners = inners.Length; + _hasCurrent = inners.Length > 0; + } + + var bag = new DisposableBag(); + for (var i = 0; i < inners.Length; i++) + { + bag.Add(inners[i].Subscribe(new InnerObserver(this, id))); + } + + _innerGroup.Disposable = bag; + } + + /// Records outer completion and completes the result if no inner is active. + private void OnOuterCompleted() + { + lock (_gate) + { + _outerDone = true; + if (!_hasCurrent) + { + downstream.OnCompleted(); + } + } + } + + /// Forwards a value from a current-generation inner, ignoring stale generations. + /// The id of the inner set that produced the value. + /// The value to forward. + private void OnNextInner(int id, TResult value) + { + lock (_gate) + { + if (id != _index) + { + return; + } + + downstream.OnNext(value); + } + } + + /// Handles inner completion, completing the result when the current set and outer are done. + /// The id of the inner set whose member completed. + private void OnInnerCompleted(int id) + { + lock (_gate) + { + if (id != _index || --_activeInners != 0) + { + return; + } + + _hasCurrent = false; + if (_outerDone) + { + downstream.OnCompleted(); + } + } + } + + /// Routes one inner observable's notifications back to the sink, tagged with its set id. + /// The owning sink. + /// The id identifying this inner subscription's generation. + private sealed class InnerObserver(Sink parent, int id) : IObserver + { + /// + public void OnNext(TResult value) => parent.OnNextInner(id, value); + + /// + public void OnError(Exception error) => parent.OnError(error); + + /// + public void OnCompleted() => parent.OnInnerCompleted(id); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyObservableSwitchSink.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyObservableSwitchSink.cs new file mode 100644 index 0000000000..8a5a73076a --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyObservableSwitchSink.cs @@ -0,0 +1,156 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// The arity-1 WhenAnyObservable sink: observes an outer stream of inner observables and +/// forwards values from the most recent inner, unsubscribing from the previous inner each +/// time a new one arrives. The switch logic is written inline; there is no operator call. +/// +/// The element type produced by the inner observables. +/// The outer observable whose latest inner observable is subscribed. +internal sealed class WhenAnyObservableSwitchSink(IObservable> sources) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer); + sink.Run(sources); + return sink; + } + + /// + /// Tracks the active inner subscription and forwards only the latest inner's values. + /// + /// The observer receiving the switched values. + private sealed class Sink(IObserver downstream) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes outer and inner notifications. + private readonly Lock _gate = new(); +#else + /// Serializes outer and inner notifications. + private readonly object _gate = new(); +#endif + + /// Holds the current inner subscription; disposed when a new inner arrives. + private readonly SwapDisposable _inner = new(); + + /// The subscription to the outer observable. + private IDisposable? _outer; + + /// Monotonic id of the current inner; notifications from older inners are ignored. + private int _index; + + /// Whether an inner observable is currently active. + private bool _hasCurrent; + + /// Whether the outer observable has completed. + private bool _outerDone; + + /// Subscribes to the outer observable. + /// The outer observable of inner observables. + public void Run(IObservable> source) => + _outer = source.Subscribe(new DelegateObserver>(OnNextOuter, OnError, OnOuterCompleted)); + + /// Forwards an error from the outer or current inner and tears down. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// + public void Dispose() + { + _outer?.Dispose(); + _inner.Dispose(); + } + + /// Switches to a newly produced inner observable, dropping the previous one. + /// The new inner observable to subscribe. + private void OnNextOuter(IObservable inner) + { + int id; + lock (_gate) + { + id = ++_index; + _hasCurrent = true; + } + + _inner.Disposable = inner.Subscribe(new InnerObserver(this, id)); + } + + /// Records outer completion and completes the result if no inner is active. + private void OnOuterCompleted() + { + lock (_gate) + { + _outerDone = true; + if (!_hasCurrent) + { + downstream.OnCompleted(); + } + } + } + + /// Forwards a value from the current inner, ignoring stale inners. + /// The id of the inner that produced the value. + /// The value to forward. + private void OnNextInner(int id, TResult value) + { + lock (_gate) + { + if (id != _index) + { + return; + } + + downstream.OnNext(value); + } + } + + /// Handles completion of the current inner, completing the result if the outer is done. + /// The id of the inner that completed. + private void OnInnerCompleted(int id) + { + lock (_gate) + { + if (id != _index) + { + return; + } + + _hasCurrent = false; + if (_outerDone) + { + downstream.OnCompleted(); + } + } + } + + /// Routes one inner observable's notifications back to the sink, tagged with its id. + /// The owning sink. + /// The id identifying this inner subscription. + private sealed class InnerObserver(Sink parent, int id) : IObserver + { + /// + public void OnNext(TResult value) => parent.OnNextInner(id, value); + + /// + public void OnError(Exception error) => parent.OnError(error); + + /// + public void OnCompleted() => parent.OnInnerCompleted(id); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity1.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity1.cs new file mode 100644 index 0000000000..a31e761f0f --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity1.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Projects a single observed property's value through a selector. The fused +/// arity-1 form of the WhenAnyValue combine sink: no gate is needed for one source. +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The type produced by the selector. +/// The source observable. +/// Projects the source element into the result. +internal sealed class WhenAnyValueSink( + IObservable> source, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } + + /// + /// Projects each source notification through the selector and forwards the result. + /// + /// The observer receiving the projected results. + /// Projects the source element into the result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver> + { + /// + public void OnNext(IObservedChange change) + { + TResult result; + try + { + result = selector(change.Value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity10.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity10.cs new file mode 100644 index 0000000000..5c6fcb00c8 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity10.cs @@ -0,0 +1,398 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 10 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The value type of observed property 6. +/// The value type of observed property 7. +/// The value type of observed property 8. +/// The value type of observed property 9. +/// The value type of observed property 10. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Source observable 10. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9, + IObservable> source10, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9, source10); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[10]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// The latest value captured from source 10. + private T10 _value10 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// Whether source 10 has produced a value yet. + private bool _has10; + + /// The number of sources that have not yet completed. + private int _active = 10; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + /// Source observable 10. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9, + IObservable> source10) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver>(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver>(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver>(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver>(On9, OnError, OnSourceCompleted)); + _subscriptions[i++] = source10.Subscribe(new DelegateObserver>(On10, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(IObservedChange change) + { + lock (_gate) + { + _value6 = change.Value; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(IObservedChange change) + { + lock (_gate) + { + _value7 = change.Value; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(IObservedChange change) + { + lock (_gate) + { + _value8 = change.Value; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(IObservedChange change) + { + lock (_gate) + { + _value9 = change.Value; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 10 and emits when every source is ready. + /// The notification from source 10. + public void On10(IObservedChange change) + { + lock (_gate) + { + _value10 = change.Value; + _has10 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9, _value10); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity11.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity11.cs new file mode 100644 index 0000000000..884efe0034 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity11.cs @@ -0,0 +1,428 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 11 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The value type of observed property 6. +/// The value type of observed property 7. +/// The value type of observed property 8. +/// The value type of observed property 9. +/// The value type of observed property 10. +/// The value type of observed property 11. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Source observable 10. +/// Source observable 11. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9, + IObservable> source10, + IObservable> source11, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9, source10, source11); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + [SuppressMessage("Major Code Smell", "S1541:Methods and properties should not be too complex", Justification = "Cyclomatic complexity is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[11]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// The latest value captured from source 10. + private T10 _value10 = default!; + + /// The latest value captured from source 11. + private T11 _value11 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// Whether source 10 has produced a value yet. + private bool _has10; + + /// Whether source 11 has produced a value yet. + private bool _has11; + + /// The number of sources that have not yet completed. + private int _active = 11; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + /// Source observable 10. + /// Source observable 11. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9, + IObservable> source10, + IObservable> source11) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver>(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver>(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver>(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver>(On9, OnError, OnSourceCompleted)); + _subscriptions[i++] = source10.Subscribe(new DelegateObserver>(On10, OnError, OnSourceCompleted)); + _subscriptions[i++] = source11.Subscribe(new DelegateObserver>(On11, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(IObservedChange change) + { + lock (_gate) + { + _value6 = change.Value; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(IObservedChange change) + { + lock (_gate) + { + _value7 = change.Value; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(IObservedChange change) + { + lock (_gate) + { + _value8 = change.Value; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(IObservedChange change) + { + lock (_gate) + { + _value9 = change.Value; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 10 and emits when every source is ready. + /// The notification from source 10. + public void On10(IObservedChange change) + { + lock (_gate) + { + _value10 = change.Value; + _has10 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 11 and emits when every source is ready. + /// The notification from source 11. + public void On11(IObservedChange change) + { + lock (_gate) + { + _value11 = change.Value; + _has11 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9, _value10, _value11); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity12.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity12.cs new file mode 100644 index 0000000000..2d3d1c5076 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity12.cs @@ -0,0 +1,457 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 12 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The value type of observed property 6. +/// The value type of observed property 7. +/// The value type of observed property 8. +/// The value type of observed property 9. +/// The value type of observed property 10. +/// The value type of observed property 11. +/// The value type of observed property 12. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Source observable 10. +/// Source observable 11. +/// Source observable 12. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9, + IObservable> source10, + IObservable> source11, + IObservable> source12, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9, source10, source11, source12); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + [SuppressMessage("Major Code Smell", "S1541:Methods and properties should not be too complex", Justification = "Cyclomatic complexity is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[12]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// The latest value captured from source 10. + private T10 _value10 = default!; + + /// The latest value captured from source 11. + private T11 _value11 = default!; + + /// The latest value captured from source 12. + private T12 _value12 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// Whether source 10 has produced a value yet. + private bool _has10; + + /// Whether source 11 has produced a value yet. + private bool _has11; + + /// Whether source 12 has produced a value yet. + private bool _has12; + + /// The number of sources that have not yet completed. + private int _active = 12; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + /// Source observable 10. + /// Source observable 11. + /// Source observable 12. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9, + IObservable> source10, + IObservable> source11, + IObservable> source12) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver>(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver>(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver>(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver>(On9, OnError, OnSourceCompleted)); + _subscriptions[i++] = source10.Subscribe(new DelegateObserver>(On10, OnError, OnSourceCompleted)); + _subscriptions[i++] = source11.Subscribe(new DelegateObserver>(On11, OnError, OnSourceCompleted)); + _subscriptions[i++] = source12.Subscribe(new DelegateObserver>(On12, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(IObservedChange change) + { + lock (_gate) + { + _value6 = change.Value; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(IObservedChange change) + { + lock (_gate) + { + _value7 = change.Value; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(IObservedChange change) + { + lock (_gate) + { + _value8 = change.Value; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(IObservedChange change) + { + lock (_gate) + { + _value9 = change.Value; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has10 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 10 and emits when every source is ready. + /// The notification from source 10. + public void On10(IObservedChange change) + { + lock (_gate) + { + _value10 = change.Value; + _has10 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has11 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 11 and emits when every source is ready. + /// The notification from source 11. + public void On11(IObservedChange change) + { + lock (_gate) + { + _value11 = change.Value; + _has11 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has12)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 12 and emits when every source is ready. + /// The notification from source 12. + public void On12(IObservedChange change) + { + lock (_gate) + { + _value12 = change.Value; + _has12 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9 && _has10 && _has11)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9, _value10, _value11, _value12); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity2.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity2.cs new file mode 100644 index 0000000000..7d966e4e54 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity2.cs @@ -0,0 +1,162 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 2 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Combines the ready values into a result. +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[2]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// The number of sources that have not yet completed. + private int _active = 2; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + public void Run(IObservable> source1, IObservable> source2) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!_has2) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!_has1) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity3.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity3.cs new file mode 100644 index 0000000000..2b895589b8 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity3.cs @@ -0,0 +1,190 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 3 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Combines the ready values into a result. +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[3]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// The number of sources that have not yet completed. + private int _active = 3; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + public void Run(IObservable> source1, IObservable> source2, IObservable> source3) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity4.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity4.cs new file mode 100644 index 0000000000..11f11f5520 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity4.cs @@ -0,0 +1,222 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 4 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Combines the ready values into a result. +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[4]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// The number of sources that have not yet completed. + private int _active = 4; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity5.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity5.cs new file mode 100644 index 0000000000..1ca9fcedc8 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity5.cs @@ -0,0 +1,251 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 5 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Combines the ready values into a result. +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[5]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// The number of sources that have not yet completed. + private int _active = 5; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity6.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity6.cs new file mode 100644 index 0000000000..d356a3c20e --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity6.cs @@ -0,0 +1,280 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 6 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The value type of observed property 6. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Combines the ready values into a result. +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[6]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// The number of sources that have not yet completed. + private int _active = 6; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver>(On6, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(IObservedChange change) + { + lock (_gate) + { + _value6 = change.Value; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity7.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity7.cs new file mode 100644 index 0000000000..9a09ce077a --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity7.cs @@ -0,0 +1,310 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 7 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The value type of observed property 6. +/// The value type of observed property 7. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[7]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// The number of sources that have not yet completed. + private int _active = 7; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver>(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver>(On7, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(IObservedChange change) + { + lock (_gate) + { + _value6 = change.Value; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(IObservedChange change) + { + lock (_gate) + { + _value7 = change.Value; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity8.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity8.cs new file mode 100644 index 0000000000..e56f50a34c --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity8.cs @@ -0,0 +1,340 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 8 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The value type of observed property 6. +/// The value type of observed property 7. +/// The value type of observed property 8. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[8]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// The number of sources that have not yet completed. + private int _active = 8; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver>(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver>(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver>(On8, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(IObservedChange change) + { + lock (_gate) + { + _value6 = change.Value; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(IObservedChange change) + { + lock (_gate) + { + _value7 = change.Value; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(IObservedChange change) + { + lock (_gate) + { + _value8 = change.Value; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity9.cs b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity9.cs new file mode 100644 index 0000000000..65c71cfdf3 --- /dev/null +++ b/src/ReactiveUI/Internal/WhenAny/WhenAnyValueSink.Arity9.cs @@ -0,0 +1,369 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Internal; + +/// +/// Combines 9 observed property values, emitting the selector result once every +/// property has produced a value. Each per-source observer extracts the change value +/// inline and bails on a plain if when peers are not ready (no-alloc fast path). +/// +/// The type of the object whose properties are observed. +/// The value type of observed property 1. +/// The value type of observed property 2. +/// The value type of observed property 3. +/// The value type of observed property 4. +/// The value type of observed property 5. +/// The value type of observed property 6. +/// The value type of observed property 7. +/// The value type of observed property 8. +/// The value type of observed property 9. +/// The type produced by the selector. +/// Source observable 1. +/// Source observable 2. +/// Source observable 3. +/// Source observable 4. +/// Source observable 5. +/// Source observable 6. +/// Source observable 7. +/// Source observable 8. +/// Source observable 9. +/// Combines the ready values into a result. +[SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] +internal sealed class WhenAnyValueSink( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9, + Func selector) : IObservable +{ + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + sink.Run(source1, source2, source3, source4, source5, source6, source7, source8, source9); + return sink; + } + + /// + /// Captures the latest value of each source under a single gate and emits the + /// selector result once every source is ready. + /// + /// The observer receiving the combined results. + /// Combines the ready values into a result. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Parameter count is inherent to the arity of this WhenAny sink.")] + private sealed class Sink(IObserver downstream, Func selector) : IDisposable + { +#if NET9_0_OR_GREATER + /// Serializes value capture and emission across the sources. + private readonly Lock _gate = new(); +#else + /// Serializes value capture and emission across the sources. + private readonly object _gate = new(); +#endif + + /// The per-source subscriptions, disposed together on teardown. + private readonly IDisposable?[] _subscriptions = new IDisposable?[9]; + + /// The latest value captured from source 1. + private T1 _value1 = default!; + + /// The latest value captured from source 2. + private T2 _value2 = default!; + + /// The latest value captured from source 3. + private T3 _value3 = default!; + + /// The latest value captured from source 4. + private T4 _value4 = default!; + + /// The latest value captured from source 5. + private T5 _value5 = default!; + + /// The latest value captured from source 6. + private T6 _value6 = default!; + + /// The latest value captured from source 7. + private T7 _value7 = default!; + + /// The latest value captured from source 8. + private T8 _value8 = default!; + + /// The latest value captured from source 9. + private T9 _value9 = default!; + + /// Whether source 1 has produced a value yet. + private bool _has1; + + /// Whether source 2 has produced a value yet. + private bool _has2; + + /// Whether source 3 has produced a value yet. + private bool _has3; + + /// Whether source 4 has produced a value yet. + private bool _has4; + + /// Whether source 5 has produced a value yet. + private bool _has5; + + /// Whether source 6 has produced a value yet. + private bool _has6; + + /// Whether source 7 has produced a value yet. + private bool _has7; + + /// Whether source 8 has produced a value yet. + private bool _has8; + + /// Whether source 9 has produced a value yet. + private bool _has9; + + /// The number of sources that have not yet completed. + private int _active = 9; + + /// Subscribes to every source, wiring each notification back into the sink. + /// Source observable 1. + /// Source observable 2. + /// Source observable 3. + /// Source observable 4. + /// Source observable 5. + /// Source observable 6. + /// Source observable 7. + /// Source observable 8. + /// Source observable 9. + public void Run( + IObservable> source1, + IObservable> source2, + IObservable> source3, + IObservable> source4, + IObservable> source5, + IObservable> source6, + IObservable> source7, + IObservable> source8, + IObservable> source9) + { + var i = 0; + _subscriptions[i++] = source1.Subscribe(new DelegateObserver>(On1, OnError, OnSourceCompleted)); + _subscriptions[i++] = source2.Subscribe(new DelegateObserver>(On2, OnError, OnSourceCompleted)); + _subscriptions[i++] = source3.Subscribe(new DelegateObserver>(On3, OnError, OnSourceCompleted)); + _subscriptions[i++] = source4.Subscribe(new DelegateObserver>(On4, OnError, OnSourceCompleted)); + _subscriptions[i++] = source5.Subscribe(new DelegateObserver>(On5, OnError, OnSourceCompleted)); + _subscriptions[i++] = source6.Subscribe(new DelegateObserver>(On6, OnError, OnSourceCompleted)); + _subscriptions[i++] = source7.Subscribe(new DelegateObserver>(On7, OnError, OnSourceCompleted)); + _subscriptions[i++] = source8.Subscribe(new DelegateObserver>(On8, OnError, OnSourceCompleted)); + _subscriptions[i++] = source9.Subscribe(new DelegateObserver>(On9, OnError, OnSourceCompleted)); + } + + /// Captures the value from source 1 and emits when every source is ready. + /// The notification from source 1. + public void On1(IObservedChange change) + { + lock (_gate) + { + _value1 = change.Value; + _has1 = true; + if (!(_has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 2 and emits when every source is ready. + /// The notification from source 2. + public void On2(IObservedChange change) + { + lock (_gate) + { + _value2 = change.Value; + _has2 = true; + if (!(_has1 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 3 and emits when every source is ready. + /// The notification from source 3. + public void On3(IObservedChange change) + { + lock (_gate) + { + _value3 = change.Value; + _has3 = true; + if (!(_has1 && _has2 && _has4 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 4 and emits when every source is ready. + /// The notification from source 4. + public void On4(IObservedChange change) + { + lock (_gate) + { + _value4 = change.Value; + _has4 = true; + if (!(_has1 && _has2 && _has3 && _has5 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 5 and emits when every source is ready. + /// The notification from source 5. + public void On5(IObservedChange change) + { + lock (_gate) + { + _value5 = change.Value; + _has5 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has6 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 6 and emits when every source is ready. + /// The notification from source 6. + public void On6(IObservedChange change) + { + lock (_gate) + { + _value6 = change.Value; + _has6 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has7 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 7 and emits when every source is ready. + /// The notification from source 7. + public void On7(IObservedChange change) + { + lock (_gate) + { + _value7 = change.Value; + _has7 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has8 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 8 and emits when every source is ready. + /// The notification from source 8. + public void On8(IObservedChange change) + { + lock (_gate) + { + _value8 = change.Value; + _has8 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has9)) + { + return; + } + + Emit(); + } + } + + /// Captures the value from source 9 and emits when every source is ready. + /// The notification from source 9. + public void On9(IObservedChange change) + { + lock (_gate) + { + _value9 = change.Value; + _has9 = true; + if (!(_has1 && _has2 && _has3 && _has4 && _has5 && _has6 && _has7 && _has8)) + { + return; + } + + Emit(); + } + } + + /// Forwards an error from any source and tears down the subscriptions. + /// The error to forward. + public void OnError(Exception error) + { + lock (_gate) + { + downstream.OnError(error); + } + + Dispose(); + } + + /// Completes the result once every source has completed. + public void OnSourceCompleted() + { + lock (_gate) + { + if (--_active == 0) + { + downstream.OnCompleted(); + } + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Emits the combined selector result once every source is ready. + private void Emit() + { + TResult result; + try + { + result = selector(_value1, _value2, _value3, _value4, _value5, _value6, _value7, _value8, _value9); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + } +} diff --git a/src/ReactiveUI/Mixins/AutoPersistHelper.cs b/src/ReactiveUI/Mixins/AutoPersistHelper.cs index 0b5fc04d57..6e139dfd69 100644 --- a/src/ReactiveUI/Mixins/AutoPersistHelper.cs +++ b/src/ReactiveUI/Mixins/AutoPersistHelper.cs @@ -1,16 +1,18 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using DynamicData; -using DynamicData.Binding; - using ReactiveUI.Builder; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -33,13 +35,35 @@ public static class AutoPersistHelper /// the generic type does not match the runtime type of the instance. /// /// +#if NET8_0_OR_GREATER + private static readonly ConditionalWeakTable PersistMetadataByType = []; +#else private static readonly ConditionalWeakTable PersistMetadataByType = new(); +#endif /// /// Initializes static members of the class. /// static AutoPersistHelper() => RxAppBuilder.EnsureInitialized(); + /// + /// AutoPersist automatically calls a method whenever the object changes. + /// + /// The reactive object type. + /// The reactive object to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// A disposable to disable automatic persistence. + [RequiresUnreferencedCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + [RequiresDynamicCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + public static IDisposable AutoPersist< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] + T>( + this T @this, + Func> doPersist) + where T : IReactiveObject + => @this.AutoPersist(doPersist, interval: null); + /// /// AutoPersist allows you to automatically call a method when an object /// has changed, throttling on a certain interval. Note that this object @@ -72,13 +96,37 @@ public static class AutoPersistHelper "AutoPersist may reflect over the runtime type when it differs from T. In trimmed/AOT builds, required property/attribute metadata " + "may be removed unless explicitly preserved. Prefer the overloads that accept AutoPersistMetadata to avoid runtime reflection.")] public static IDisposable AutoPersist< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] + T>( this T @this, Func> doPersist, - TimeSpan? interval = null) - where T : IReactiveObject + TimeSpan? interval) + where T : IReactiveObject => @this.AutoPersist(doPersist, Observable.Never, interval); + /// + /// AutoPersist automatically calls a method whenever the object changes or a manual save is signalled. + /// + /// The reactive object type. + /// The save signal type. + /// The reactive object to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// A disposable to disable automatic persistence. + [RequiresUnreferencedCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + [RequiresDynamicCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + public static IDisposable AutoPersist< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] + T, + TDontCare>( + this T @this, + Func> doPersist, + IObservable manualSaveSignal) + where T : IReactiveObject + => @this.AutoPersist(doPersist, manualSaveSignal, interval: null); + /// /// AutoPersist allows you to automatically call a method when an object /// has changed, throttling on a certain interval. Note that this object @@ -116,13 +164,15 @@ public static IDisposable AutoPersist< "AutoPersist may reflect over the runtime type when it differs from T. In trimmed/AOT builds, required property/attribute metadata " + "may be removed unless explicitly preserved. Prefer the overloads that accept AutoPersistMetadata to avoid runtime reflection.")] public static IDisposable AutoPersist< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] + T, TDontCare>( this T @this, Func> doPersist, IObservable manualSaveSignal, - TimeSpan? interval = null) - where T : IReactiveObject + TimeSpan? interval) + where T : IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(@this); ArgumentExceptionHelper.ThrowIfNull(doPersist); @@ -130,8 +180,6 @@ public static IDisposable AutoPersist< interval ??= TimeSpan.FromSeconds(3.0); - // Fast path: if T is the actual runtime type, use per-closed-generic cache (no CWT lookup). - // Slow path: preserve historical semantics by reflecting over the runtime type. var runtimeType = @this.GetType(); var metadata = runtimeType == typeof(T) ? PersistMetadataHolder.Metadata @@ -144,21 +192,118 @@ public static IDisposable AutoPersist< var persistablePropertyNames = metadata.PersistablePropertyNames; - var saveHint = - @this.GetChangedObservable() - .Where(x => x.PropertyName is not null && persistablePropertyNames.Contains(x.PropertyName)) - .Select(static _ => Unit.Default) - .Merge(manualSaveSignal.Select(static _ => Unit.Default)); - - var autoSaver = - saveHint - .Throttle(interval.Value, RxSchedulers.TaskpoolScheduler) - .SelectMany(_ => doPersist(@this)) - .Publish(); - - // NB: This rigamarole is to prevent the initialization of a class - // from triggering a save. - var ret = new SingleAssignmentDisposable(); + var ret = new OnceDisposable(); + RxSchedulers.MainThreadScheduler.Schedule(() => + { + if (ret.IsDisposed) + { + return; + } + + ret.Disposable = new AutoPersistDriver( + @this, + doPersist, + persistablePropertyNames, + manualSaveSignal, + interval.Value); + }); + + return ret; + } + + /// + /// AutoPersist overload that uses explicit metadata and performs no runtime reflection. + /// + /// The reactive object type. + /// The reactive object to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// The persistence metadata to use for determining persistable properties. + /// A disposable to disable automatic persistence. + public static IDisposable AutoPersist( + this T @this, + Func> doPersist, + AutoPersistMetadata metadata) + where T : IReactiveObject + => @this.AutoPersist(doPersist, metadata, interval: null); + + /// + /// AutoPersist overload that performs no runtime reflection and is suitable for trimming/AOT scenarios. + /// + /// The reactive object type. + /// The reactive object to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// The persistence metadata to use for determining persistable properties. + /// The interval to save the object on. + /// A disposable to disable automatic persistence. + /// Thrown when indicates the object is not persistable. + public static IDisposable AutoPersist( + this T @this, + Func> doPersist, + AutoPersistMetadata metadata, + TimeSpan? interval) + where T : IReactiveObject + => @this.AutoPersist(doPersist, Observable.Never, metadata, interval); + + /// + /// AutoPersist overload that uses explicit metadata and a manual save signal, performing no runtime reflection. + /// + /// The reactive object type. + /// The save signal type. + /// The reactive object to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// The persistence metadata to use for determining persistable properties. + /// A disposable to disable automatic persistence. + public static IDisposable AutoPersist( + this T @this, + Func> doPersist, + IObservable manualSaveSignal, + AutoPersistMetadata metadata) + where T : IReactiveObject + => @this.AutoPersist(doPersist, manualSaveSignal, metadata, interval: null); + + /// + /// AutoPersist overload that performs no runtime reflection and is suitable for trimming/AOT scenarios. + /// + /// The reactive object type. + /// The save signal type. + /// The reactive object to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// + /// When invoked, the object will be saved regardless of whether it has changed. + /// + /// The persistence metadata to use for determining persistable properties. + /// + /// The interval to save the object on. Note that if an object is constantly changing, + /// it is possible that it will never be saved. + /// + /// A disposable to disable automatic persistence. + /// Thrown when indicates the object is not persistable. + public static IDisposable AutoPersist( + this T @this, + Func> doPersist, + IObservable manualSaveSignal, + AutoPersistMetadata metadata, + TimeSpan? interval) + where T : IReactiveObject + { + ArgumentExceptionHelper.ThrowIfNull(@this); + ArgumentExceptionHelper.ThrowIfNull(doPersist); + ArgumentExceptionHelper.ThrowIfNull(manualSaveSignal); + ArgumentExceptionHelper.ThrowIfNull(metadata); + + if (!metadata.HasDataContract) + { + throw new ArgumentException( + "AutoPersist can only be applied to objects with [DataContract]", + nameof(metadata)); + } + + interval ??= TimeSpan.FromSeconds(3.0); + + var persistablePropertyNames = metadata.PersistablePropertyNames; + + var ret = new OnceDisposable(); RxSchedulers.MainThreadScheduler.Schedule(() => { if (ret.IsDisposed) @@ -166,12 +311,32 @@ public static IDisposable AutoPersist< return; } - ret.Disposable = autoSaver.Connect(); + ret.Disposable = new AutoPersistDriver( + @this, + doPersist, + persistablePropertyNames, + manualSaveSignal, + interval.Value); }); return ret; } + /// + /// Applies AutoPersistence to all objects in a collection using explicit persistence metadata. + /// + /// The item type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// The persistence metadata that determines which properties trigger persistence. + /// A disposable to disable automatic persistence. + public static IDisposable AutoPersistCollection( + this ObservableCollection @this, + Func> doPersist, + AutoPersistMetadata metadata) + where TItem : IReactiveObject + => @this.AutoPersistCollection(doPersist, metadata, interval: null); + /// /// Apply AutoPersistence to all objects in a collection using explicit persistence metadata. /// This overload performs no runtime reflection and is suitable for trimming/AOT scenarios. @@ -192,10 +357,28 @@ public static IDisposable AutoPersistCollection( this ObservableCollection @this, Func> doPersist, AutoPersistMetadata metadata, - TimeSpan? interval = null) - where TItem : IReactiveObject + TimeSpan? interval) + where TItem : IReactiveObject => AutoPersistCollection(@this, doPersist, Observable.Never, metadata, interval); + /// + /// Applies AutoPersistence to all objects in a collection using explicit persistence metadata. + /// + /// The item type. + /// The manual save signal type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// The persistence metadata that determines which properties trigger persistence. + /// A disposable to disable automatic persistence. + public static IDisposable AutoPersistCollection( + this ObservableCollection @this, + Func> doPersist, + IObservable manualSaveSignal, + AutoPersistMetadata metadata) + where TItem : IReactiveObject + => @this.AutoPersistCollection(doPersist, manualSaveSignal, metadata, interval: null); + /// /// Apply AutoPersistence to all objects in a collection using explicit persistence metadata. /// This overload performs no runtime reflection and is suitable for trimming/AOT scenarios. @@ -216,9 +399,34 @@ public static IDisposable AutoPersistCollection( Func> doPersist, IObservable manualSaveSignal, AutoPersistMetadata metadata, - TimeSpan? interval = null) - where TItem : IReactiveObject - => AutoPersistCollection, TDontCare>(@this, doPersist, manualSaveSignal, metadata, interval); + TimeSpan? interval) + where TItem : IReactiveObject + => AutoPersistCollection, TDontCare>( + @this, + doPersist, + manualSaveSignal, + metadata, + interval); + + /// + /// Applies AutoPersistence to all objects in a collection using explicit persistence metadata. + /// + /// The item type. + /// The collection type. + /// The manual save signal type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// The persistence metadata that determines which properties trigger persistence. + /// A disposable to disable automatic persistence. + public static IDisposable AutoPersistCollection( + this TCollection @this, + Func> doPersist, + IObservable manualSaveSignal, + AutoPersistMetadata metadata) + where TItem : IReactiveObject + where TCollection : INotifyCollectionChanged, IEnumerable + => @this.AutoPersistCollection(doPersist, manualSaveSignal, metadata, interval: null); /// /// Apply AutoPersistence to all objects in a collection using explicit persistence metadata. @@ -241,9 +449,9 @@ public static IDisposable AutoPersistCollection( Func> doPersist, IObservable manualSaveSignal, AutoPersistMetadata metadata, - TimeSpan? interval = null) - where TItem : IReactiveObject - where TCollection : INotifyCollectionChanged, IEnumerable + TimeSpan? interval) + where TItem : IReactiveObject + where TCollection : INotifyCollectionChanged, IEnumerable { ArgumentExceptionHelper.ThrowIfNull(@this); ArgumentExceptionHelper.ThrowIfNull(doPersist); @@ -252,13 +460,15 @@ public static IDisposable AutoPersistCollection( if (!metadata.HasDataContract) { - throw new ArgumentException("AutoPersist can only be applied to objects with [DataContract]", nameof(metadata)); + throw new ArgumentException( + "AutoPersist can only be applied to objects with [DataContract]", + nameof(metadata)); } - var disposerList = new Dictionary(); + Dictionary disposerList = []; var subscription = @this.ActOnEveryObject( - onAdd: x => + x => { if (disposerList.ContainsKey(x)) { @@ -267,7 +477,7 @@ public static IDisposable AutoPersistCollection( disposerList[x] = x.AutoPersist(doPersist, manualSaveSignal, metadata, interval); }, - onRemove: x => + x => { if (!disposerList.TryGetValue(x, out var d)) { @@ -278,7 +488,7 @@ public static IDisposable AutoPersistCollection( disposerList.Remove(x); }); - return Disposable.Create(() => + return new ActionDisposable(() => { subscription.Dispose(); @@ -291,6 +501,24 @@ public static IDisposable AutoPersistCollection( }); } + /// + /// Applies AutoPersistence to all objects in a read-only collection using explicit persistence metadata. + /// + /// The item type. + /// The manual save signal type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// The persistence metadata that determines which properties trigger persistence. + /// A disposable to disable automatic persistence. + public static IDisposable AutoPersistCollection( + this ReadOnlyObservableCollection @this, + Func> doPersist, + IObservable manualSaveSignal, + AutoPersistMetadata metadata) + where TItem : IReactiveObject + => @this.AutoPersistCollection(doPersist, manualSaveSignal, metadata, interval: null); + /// /// Apply AutoPersistence to all objects in a read-only collection using explicit persistence metadata. /// This overload performs no runtime reflection and is suitable for trimming/AOT scenarios. @@ -311,9 +539,34 @@ public static IDisposable AutoPersistCollection( Func> doPersist, IObservable manualSaveSignal, AutoPersistMetadata metadata, - TimeSpan? interval = null) - where TItem : IReactiveObject - => AutoPersistCollection, TDontCare>(@this, doPersist, manualSaveSignal, metadata, interval); + TimeSpan? interval) + where TItem : IReactiveObject + => AutoPersistCollection, TDontCare>( + @this, + doPersist, + manualSaveSignal, + metadata, + interval); + + /// + /// Applies AutoPersistence to all objects in a collection using a metadata provider. + /// + /// The item type. + /// The collection type. + /// The manual save signal type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// A function that returns the persistence metadata to use for a specific item instance. + /// A disposable to disable automatic persistence. + public static IDisposable AutoPersistCollection( + this TCollection @this, + Func> doPersist, + IObservable manualSaveSignal, + Func metadataProvider) + where TItem : IReactiveObject + where TCollection : INotifyCollectionChanged, IEnumerable + => @this.AutoPersistCollection(doPersist, manualSaveSignal, metadataProvider, interval: null); /// /// Apply AutoPersistence to all objects in a collection using a metadata provider. @@ -339,30 +592,29 @@ public static IDisposable AutoPersistCollection( Func> doPersist, IObservable manualSaveSignal, Func metadataProvider, - TimeSpan? interval = null) - where TItem : IReactiveObject - where TCollection : INotifyCollectionChanged, IEnumerable + TimeSpan? interval) + where TItem : IReactiveObject + where TCollection : INotifyCollectionChanged, IEnumerable { ArgumentExceptionHelper.ThrowIfNull(@this); ArgumentExceptionHelper.ThrowIfNull(doPersist); ArgumentExceptionHelper.ThrowIfNull(manualSaveSignal); ArgumentExceptionHelper.ThrowIfNull(metadataProvider); - var disposerList = new Dictionary(); + Dictionary disposerList = []; var subscription = @this.ActOnEveryObject( - onAdd: x => + x => { if (disposerList.ContainsKey(x)) { return; } - // Non-RUC path: caller provides metadata explicitly. var metadata = metadataProvider(x); disposerList[x] = x.AutoPersist(doPersist, manualSaveSignal, metadata, interval); }, - onRemove: x => + x => { if (!disposerList.TryGetValue(x, out var d)) { @@ -373,7 +625,7 @@ public static IDisposable AutoPersistCollection( disposerList.Remove(x); }); - return Disposable.Create(() => + return new ActionDisposable(() => { subscription.Dispose(); @@ -387,118 +639,19 @@ public static IDisposable AutoPersistCollection( } /// - /// Creates a metadata provider for homogeneous collections where is the concrete runtime type. - /// This helper performs no runtime reflection and is suitable for trimming/AOT scenarios. + /// Applies AutoPersistence to all objects in a collection. /// /// The item type. - /// A function returning metadata for . - public static Func CreateMetadataProvider< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] TItem>() - where TItem : IReactiveObject - { - var metadata = CreateMetadata(); - return _ => metadata; - } - - /// - /// AutoPersist overload that performs no runtime reflection and is suitable for trimming/AOT scenarios. - /// - /// The reactive object type. - /// The reactive object to watch for changes. - /// The asynchronous method to call to save the object to disk. - /// The persistence metadata to use for determining persistable properties. - /// The interval to save the object on. - /// A disposable to disable automatic persistence. - /// Thrown when indicates the object is not persistable. - public static IDisposable AutoPersist( - this T @this, - Func> doPersist, - AutoPersistMetadata metadata, - TimeSpan? interval = null) - where T : IReactiveObject - => @this.AutoPersist(doPersist, Observable.Never, metadata, interval); - - /// - /// AutoPersist overload that performs no runtime reflection and is suitable for trimming/AOT scenarios. - /// - /// The reactive object type. - /// The save signal type. - /// The reactive object to watch for changes. + /// The reactive collection to watch for changes. /// The asynchronous method to call to save the object to disk. - /// - /// When invoked, the object will be saved regardless of whether it has changed. - /// - /// The persistence metadata to use for determining persistable properties. - /// - /// The interval to save the object on. Note that if an object is constantly changing, - /// it is possible that it will never be saved. - /// /// A disposable to disable automatic persistence. - /// Thrown when indicates the object is not persistable. - public static IDisposable AutoPersist( - this T @this, - Func> doPersist, - IObservable manualSaveSignal, - AutoPersistMetadata metadata, - TimeSpan? interval = null) - where T : IReactiveObject - { - ArgumentExceptionHelper.ThrowIfNull(@this); - ArgumentExceptionHelper.ThrowIfNull(doPersist); - ArgumentExceptionHelper.ThrowIfNull(manualSaveSignal); - ArgumentExceptionHelper.ThrowIfNull(metadata); - - if (!metadata.HasDataContract) - { - throw new ArgumentException("AutoPersist can only be applied to objects with [DataContract]", nameof(metadata)); - } - - interval ??= TimeSpan.FromSeconds(3.0); - - var persistablePropertyNames = metadata.PersistablePropertyNames; - - var saveHint = - @this.GetChangedObservable() - .Where(x => x.PropertyName is not null && persistablePropertyNames.Contains(x.PropertyName)) - .Select(static _ => Unit.Default) - .Merge(manualSaveSignal.Select(static _ => Unit.Default)); - - var autoSaver = - saveHint - .Throttle(interval.Value, RxSchedulers.TaskpoolScheduler) - .SelectMany(_ => doPersist(@this)) - .Publish(); - - var ret = new SingleAssignmentDisposable(); - RxSchedulers.MainThreadScheduler.Schedule(() => - { - if (ret.IsDisposed) - { - return; - } - - ret.Disposable = autoSaver.Connect(); - }); - - return ret; - } - - /// - /// Creates trimming/AOT-friendly persistence metadata for . - /// - /// - /// The type to analyze for [DataContract] and [DataMember]. - /// - /// The computed persistence metadata. - /// - /// This method is analyzable by the trimmer due to the - /// on - /// and uses no runtime type discovery. - /// - public static AutoPersistMetadata CreateMetadata< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>() - where T : IReactiveObject - => PersistMetadataHolder.Metadata.Public; + [RequiresUnreferencedCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + [RequiresDynamicCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + public static IDisposable AutoPersistCollection( + this ObservableCollection @this, + Func> doPersist) + where TItem : IReactiveObject + => @this.AutoPersistCollection(doPersist, interval: null); /// /// Apply AutoPersistence to all objects in a collection. Items that are @@ -528,10 +681,28 @@ public static AutoPersistMetadata CreateMetadata< public static IDisposable AutoPersistCollection( this ObservableCollection @this, Func> doPersist, - TimeSpan? interval = null) - where TItem : IReactiveObject + TimeSpan? interval) + where TItem : IReactiveObject => AutoPersistCollection(@this, doPersist, Observable.Never, interval); + /// + /// Applies AutoPersistence to all objects in a collection. + /// + /// The item type. + /// The return signal type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// A disposable to disable automatic persistence. + [RequiresUnreferencedCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + [RequiresDynamicCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + public static IDisposable AutoPersistCollection( + this ObservableCollection @this, + Func> doPersist, + IObservable manualSaveSignal) + where TItem : IReactiveObject + => @this.AutoPersistCollection(doPersist, manualSaveSignal, interval: null); + /// /// Apply AutoPersistence to all objects in a collection. Items that are /// no longer in the collection won't be persisted anymore. @@ -565,9 +736,31 @@ public static IDisposable AutoPersistCollection( this ObservableCollection @this, Func> doPersist, IObservable manualSaveSignal, - TimeSpan? interval = null) - where TItem : IReactiveObject - => AutoPersistCollection, TDontCare>(@this, doPersist, manualSaveSignal, interval); + TimeSpan? interval) + where TItem : IReactiveObject + => AutoPersistCollection, TDontCare>( + @this, + doPersist, + manualSaveSignal, + interval); + + /// + /// Applies AutoPersistence to all objects in a read-only collection. + /// + /// The item type. + /// The signal type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// A disposable to disable automatic persistence. + [RequiresUnreferencedCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + [RequiresDynamicCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + public static IDisposable AutoPersistCollection( + this ReadOnlyObservableCollection @this, + Func> doPersist, + IObservable manualSaveSignal) + where TItem : IReactiveObject + => @this.AutoPersistCollection(doPersist, manualSaveSignal, interval: null); /// /// Apply AutoPersistence to all objects in a collection. Items that are @@ -602,9 +795,33 @@ public static IDisposable AutoPersistCollection( this ReadOnlyObservableCollection @this, Func> doPersist, IObservable manualSaveSignal, - TimeSpan? interval = null) - where TItem : IReactiveObject - => AutoPersistCollection, TDontCare>(@this, doPersist, manualSaveSignal, interval); + TimeSpan? interval) + where TItem : IReactiveObject + => AutoPersistCollection, TDontCare>( + @this, + doPersist, + manualSaveSignal, + interval); + + /// + /// Applies AutoPersistence to all objects in a collection. + /// + /// The item type. + /// The collection type. + /// The signal type. + /// The reactive collection to watch for changes. + /// The asynchronous method to call to save the object to disk. + /// When invoked, the object will be saved regardless of whether it has changed. + /// A disposable to disable automatic persistence. + [RequiresUnreferencedCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + [RequiresDynamicCode("AutoPersist may reflect over the runtime type; prefer the AutoPersistMetadata overloads for trimming/AOT.")] + public static IDisposable AutoPersistCollection( + this TCollection @this, + Func> doPersist, + IObservable manualSaveSignal) + where TItem : IReactiveObject + where TCollection : INotifyCollectionChanged, IEnumerable + => @this.AutoPersistCollection(doPersist, manualSaveSignal, interval: null); /// /// Apply AutoPersistence to all objects in a collection. Items that are @@ -635,19 +852,18 @@ public static IDisposable AutoPersistCollection( this TCollection @this, Func> doPersist, IObservable manualSaveSignal, - TimeSpan? interval = null) - where TItem : IReactiveObject - where TCollection : INotifyCollectionChanged, IEnumerable + TimeSpan? interval) + where TItem : IReactiveObject + where TCollection : INotifyCollectionChanged, IEnumerable { ArgumentExceptionHelper.ThrowIfNull(@this); ArgumentExceptionHelper.ThrowIfNull(doPersist); ArgumentExceptionHelper.ThrowIfNull(manualSaveSignal); - // Dictionary is used to preserve prior semantics: per-item disposable tracked by item instance. - var disposerList = new Dictionary(); + Dictionary disposerList = []; var subscription = @this.ActOnEveryObject( - onAdd: x => + x => { if (disposerList.TryGetValue(x, out _)) { @@ -656,7 +872,7 @@ public static IDisposable AutoPersistCollection( disposerList[x] = x.AutoPersist(doPersist, manualSaveSignal, interval); }, - onRemove: x => + x => { if (!disposerList.TryGetValue(x, out var d)) { @@ -667,7 +883,7 @@ public static IDisposable AutoPersistCollection( disposerList.Remove(x); }); - return Disposable.Create(() => + return new ActionDisposable(() => { subscription.Dispose(); @@ -680,6 +896,49 @@ public static IDisposable AutoPersistCollection( }); } + /// + /// Creates a metadata provider for homogeneous collections where is the concrete runtime type. + /// This helper performs no runtime reflection and is suitable for trimming/AOT scenarios. + /// + /// The item type. + /// A function returning metadata for . + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static Func CreateMetadataProvider< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] + TItem>() + where TItem : IReactiveObject + { + var metadata = CreateMetadata(); + return _ => metadata; + } + + /// + /// Creates trimming/AOT-friendly persistence metadata for . + /// + /// + /// The type to analyze for [DataContract] and [DataMember]. + /// + /// The computed persistence metadata. + /// + /// This method is analyzable by the trimmer due to the + /// on + /// and uses no runtime type discovery. + /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static AutoPersistMetadata CreateMetadata< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] + T>() + where T : IReactiveObject + => PersistMetadataHolder.Metadata.Public; + /// /// Call methods and whenever an object is added or /// removed from a collection. This method correctly handles both when @@ -694,7 +953,7 @@ public static IDisposable ActOnEveryObject( this ObservableCollection @this, Action onAdd, Action onRemove) - where TItem : IReactiveObject + where TItem : IReactiveObject => ActOnEveryObject>(@this, onAdd, onRemove); /// @@ -711,7 +970,7 @@ public static IDisposable ActOnEveryObject( this ReadOnlyObservableCollection @this, Action onAdd, Action onRemove) - where TItem : IReactiveObject + where TItem : IReactiveObject => ActOnEveryObject>(@this, onAdd, onRemove); /// @@ -729,17 +988,17 @@ public static IDisposable ActOnEveryObject( this TCollection collection, Action onAdd, Action onRemove) - where TItem : IReactiveObject - where TCollection : INotifyCollectionChanged, IEnumerable + where TItem : IReactiveObject + where TCollection : INotifyCollectionChanged, IEnumerable { ArgumentExceptionHelper.ThrowIfNull(onAdd); ArgumentExceptionHelper.ThrowIfNull(onRemove); ArgumentExceptionHelper.ThrowIfNull(collection); - // ToObservableChangeSet will emit existing items when first subscribed, so we don't need to manually iterate them - var changedDisposable = ActOnEveryObject(collection.ToObservableChangeSet(), onAdd, onRemove); + var changedDisposable = + ActOnEveryObject(collection.ToObservableChangeSet(), onAdd, onRemove); - return Disposable.Create(() => + return new ActionDisposable(() => { changedDisposable.Dispose(); @@ -761,74 +1020,66 @@ public static IDisposable ActOnEveryObject( /// A method to be called when an object is removed from the collection. /// A disposable that deactivates this behavior. public static IDisposable ActOnEveryObject( - this IObservable> @this, + this IObservable> @this, Action onAdd, Action onRemove) - where TItem : IReactiveObject + where TItem : IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(@this); ArgumentExceptionHelper.ThrowIfNull(onAdd); ArgumentExceptionHelper.ThrowIfNull(onRemove); - return @this.Subscribe(changeSet => + return @this.Subscribe(new DelegateObserver>(changeSet => { foreach (var change in changeSet) { - switch (change.Reason) + ApplyChange(change, onAdd, onRemove); + } + })); + } + + /// + /// Applies a single change-set entry by invoking the add or remove callback for the affected items. + /// + /// The item type. + /// The change to apply. + /// The callback invoked for added items. + /// The callback invoked for removed items. + private static void ApplyChange(ReactiveChange change, Action onAdd, Action onRemove) + where TItem : IReactiveObject + { + switch (change.Reason) + { + case ReactiveChangeReason.Add: + { + onAdd(change.Current); + break; + } + + case ReactiveChangeReason.Remove: + { + onRemove(change.Current); + break; + } + + case ReactiveChangeReason.Replace: + { + if (change.Previous is { } previous) { - case ListChangeReason.Refresh: - // Preserve original ordering: remove all, then add all. - foreach (var item in change.Range) - { - onRemove(item); - } - - foreach (var item in change.Range) - { - onAdd(item); - } - - break; - - case ListChangeReason.Clear: - foreach (var item in change.Range) - { - onRemove(item); - } - - break; - - case ListChangeReason.Add: - onAdd(change.Item.Current); - break; - - case ListChangeReason.AddRange: - foreach (var item in change.Range) - { - onAdd(item); - } - - break; - - case ListChangeReason.Replace: - onRemove(change.Item.Previous.Value); - onAdd(change.Item.Current); - break; - - case ListChangeReason.Remove: - onRemove(change.Item.Current); - break; - - case ListChangeReason.RemoveRange: - foreach (var item in change.Range) - { - onRemove(item); - } - - break; + onRemove(previous); } + + onAdd(change.Current); + break; } - }); + + case ReactiveChangeReason.Refresh: + { + onRemove(change.Current); + onAdd(change.Current); + break; + } + } } /// @@ -892,8 +1143,10 @@ public AutoPersistMetadata(bool hasDataContract, ISet persistablePropert /// The type for which persistence metadata is computed. /// private static class PersistMetadataHolder< - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T> - where T : IReactiveObject + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] + T> + where T : IReactiveObject { /// /// Gets the computed persistence metadata for . @@ -915,7 +1168,7 @@ private PersistMetadata(bool hasDataContract, HashSet persistablePropert { HasDataContract = hasDataContract; PersistablePropertyNames = persistablePropertyNames; - Public = new AutoPersistMetadata(hasDataContract, persistablePropertyNames); + Public = new(hasDataContract, persistablePropertyNames); } /// @@ -938,16 +1191,19 @@ private PersistMetadata(bool hasDataContract, HashSet persistablePropert /// /// The type to analyze. /// The computed persistence metadata. + [SuppressMessage( + "Security Hotspot", + "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", + Justification = "AutoPersist inspects non-public [DataMember] properties to mirror DataContract serialization semantics.")] internal static PersistMetadata Create( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { - // Preserve original semantics: [DataContract] is checked via GetCustomAttributes(..., inherit: true). - var hasDataContract = type.GetCustomAttributes(typeof(DataContractAttribute), inherit: true).Length > 0; + var hasDataContract = type.GetCustomAttributes(typeof(DataContractAttribute), true).Length > 0; - // Preserve original semantics: consider DeclaredProperties only (not inherited properties). - // Use reflection flags directly to avoid GetTypeInfo() overhead. - var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); + var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.DeclaredOnly); HashSet? set = null; @@ -959,12 +1215,12 @@ internal static PersistMetadata Create( continue; } - set ??= new HashSet(StringComparer.Ordinal); + set ??= new(StringComparer.Ordinal); set.Add(p.Name); } - set ??= new HashSet(StringComparer.Ordinal); - return new PersistMetadata(hasDataContract, set); + set ??= new(StringComparer.Ordinal); + return new(hasDataContract, set); } /// @@ -973,11 +1229,149 @@ internal static PersistMetadata Create( /// The property to inspect. /// if the property is annotated; otherwise . [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool HasDataMemberAttribute(PropertyInfo property) + private static bool HasDataMemberAttribute(PropertyInfo property) => property.IsDefined(typeof(DataMemberAttribute), true); + } + + /// + /// Watches a reactive object for persistable property changes and manual save signals, debounces them by a fixed + /// interval, and runs the persist operation. A single allocation-light sink with no intermediate observable + /// operators (replaces the prior Where/Select/Merge/Throttle/SelectMany/Publish chain). + /// + /// The reactive object type. + /// The manual save signal type. + private sealed class AutoPersistDriver : IDisposable + where T : IReactiveObject + { + /// Guards the timer, the persist subscription, and the disposed flag. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The object being persisted. + private readonly T _target; + + /// Runs the persist operation for the target. + private readonly Func> _doPersist; + + /// The property names whose changes trigger a save. + private readonly ISet _persistableNames; + + /// The quiet interval after which a save runs (debounce). + private readonly TimeSpan _interval; + + /// The debounce timer; (re)started on each save request. + private readonly Timer _timer; + + /// The subscription to the target's property-change stream. + private readonly IDisposable _changeSubscription; + + /// The subscription to the manual save signal. + private readonly IDisposable _manualSubscription; + + /// The current in-flight persist subscription, if any. + private IDisposable? _persistSubscription; + + /// Whether the driver has been disposed. + private bool _disposed; + + /// Initializes a new instance of the class and starts watching. + /// The object being persisted. + /// Runs the persist operation for the target. + /// The property names whose changes trigger a save. + /// A signal that forces a save regardless of changes. + /// The quiet interval after which a save runs. + public AutoPersistDriver( + T target, + Func> doPersist, + ISet persistableNames, + IObservable manualSaveSignal, + TimeSpan interval) + { + _target = target; + _doPersist = doPersist; + _persistableNames = persistableNames; + _interval = interval; + _timer = new Timer(OnElapsed, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + _changeSubscription = target.GetChangedObservable() + .Subscribe(new DelegateObserver>(OnPropertyChanged)); + _manualSubscription = manualSaveSignal + .Subscribe(new DelegateObserver(_ => RequestSave())); + } + + /// + public void Dispose() + { + lock (_gate) + { + if (_disposed) + { + return; + } + + _disposed = true; + } + + _changeSubscription.Dispose(); + _manualSubscription.Dispose(); + _timer.Dispose(); + _persistSubscription?.Dispose(); + } + + /// Requests a save when a persistable property changes. + /// The property-change arguments. + private void OnPropertyChanged(IReactivePropertyChangedEventArgs args) + { + if (args.PropertyName is null || !_persistableNames.Contains(args.PropertyName)) + { + return; + } + + RequestSave(); + } + + /// (Re)starts the debounce timer so a save runs after the quiet interval. + private void RequestSave() { - // Avoid LINQ allocations; use IsDefined which is efficient for the common case. - // DataMemberAttribute is not inherited by default, but preserve inherit=true for parity. - return property.IsDefined(typeof(DataMemberAttribute), inherit: true); + lock (_gate) + { + if (_disposed) + { + return; + } + + _timer.Change(_interval, Timeout.InfiniteTimeSpan); + } + } + + /// Runs the persist operation when the debounce interval elapses. + /// Unused timer state. + private void OnElapsed(object? state) + { + IObservable persist; + lock (_gate) + { + if (_disposed) + { + return; + } + + persist = _doPersist(_target); + } + + var subscription = persist.Subscribe(new DelegateObserver(static _ => { })); + lock (_gate) + { + if (_disposed) + { + subscription.Dispose(); + return; + } + + _persistSubscription?.Dispose(); + _persistSubscription = subscription; + } } } } diff --git a/src/ReactiveUI/Mixins/BuilderMixins.cs b/src/ReactiveUI/Mixins/BuilderMixins.cs index 8116ac99f4..cf4c8a3644 100644 --- a/src/ReactiveUI/Mixins/BuilderMixins.cs +++ b/src/ReactiveUI/Mixins/BuilderMixins.cs @@ -1,10 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; using System.Reflection; - using Splat.Builder; namespace ReactiveUI.Builder; @@ -44,6 +44,10 @@ public static class BuilderMixins /// ]]> /// /// + [SuppressMessage( + "Critical Code Smell", + "S3215:Interface instances should not be cast to concrete types", + Justification = "DefaultViewLocator exposes view-registration APIs not present on IViewLocator.")] public static IReactiveUIBuilder RegisterViews( this IReactiveUIBuilder builder, Action configure) @@ -52,11 +56,11 @@ public static IReactiveUIBuilder RegisterViews( ArgumentExceptionHelper.ThrowIfNull(configure); var viewLocator = AppLocator.Current.GetService() as DefaultViewLocator - ?? throw new InvalidOperationException( - "DefaultViewLocator must be registered before calling RegisterViews. " + - "Ensure you've called WithPlatformModule() or manually registered DefaultViewLocator."); + ?? throw new InvalidOperationException( + "DefaultViewLocator must be registered before calling RegisterViews. " + + "Ensure you've called WithPlatformModule() or manually registered DefaultViewLocator."); - var mappingBuilder = new ViewMappingBuilder(viewLocator); + ViewMappingBuilder mappingBuilder = new(viewLocator); configure(mappingBuilder); return builder; } @@ -89,21 +93,43 @@ public static IReactiveUIBuilder RegisterViews( /// ]]> /// /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + [SuppressMessage( + "Critical Code Smell", + "S3215:Interface instances should not be cast to concrete types", + Justification = "DefaultViewLocator exposes view-registration APIs not present on IViewLocator.")] public static IReactiveUIBuilder WithViewModule(this IReactiveUIBuilder builder) where TModule : IViewModule, new() { ArgumentExceptionHelper.ThrowIfNull(builder); var viewLocator = AppLocator.Current.GetService() as DefaultViewLocator - ?? throw new InvalidOperationException( - "DefaultViewLocator must be registered before calling WithViewModule. " + - "Ensure you've called WithPlatformModule() or manually registered DefaultViewLocator."); + ?? throw new InvalidOperationException( + "DefaultViewLocator must be registered before calling WithViewModule. " + + "Ensure you've called WithPlatformModule() or manually registered DefaultViewLocator."); - var module = new TModule(); + TModule module = new(); module.RegisterViews(viewLocator); return builder; } + /// + /// Configures the task pool scheduler, also setting the RxApp scheduler. + /// + /// The builder. + /// The scheduler. + /// + /// The builder instance for chaining. + /// + /// scheduler. + public static IReactiveUIBuilder WithTaskPoolScheduler( + this IReactiveUIBuilder builder, + IScheduler scheduler) => + WithTaskPoolScheduler(builder, scheduler, true); + /// /// Configures the task pool scheduler. /// @@ -114,7 +140,10 @@ public static IReactiveUIBuilder WithViewModule(this IReactiveUIBuilder /// The builder instance for chaining. /// /// scheduler. - public static IReactiveUIBuilder WithTaskPoolScheduler(this IReactiveUIBuilder builder, IScheduler scheduler, bool setRxApp = true) + public static IReactiveUIBuilder WithTaskPoolScheduler( + this IReactiveUIBuilder builder, + IScheduler scheduler, + bool setRxApp) { ArgumentExceptionHelper.ThrowIfNull(builder); @@ -143,6 +172,20 @@ public static IReactiveUIBuilder BuildApp(this IAppBuilder appBuilder) return reactiveUiBuilder; } + /// + /// Configures the main thread scheduler, also setting the RxApp scheduler. + /// + /// The builder. + /// The scheduler. + /// + /// The builder instance for chaining. + /// + /// builder. + public static IReactiveUIBuilder WithMainThreadScheduler( + this IReactiveUIBuilder builder, + IScheduler scheduler) => + WithMainThreadScheduler(builder, scheduler, true); + /// /// Configures the main thread scheduler. /// @@ -153,7 +196,10 @@ public static IReactiveUIBuilder BuildApp(this IAppBuilder appBuilder) /// The builder instance for chaining. /// /// builder. - public static IReactiveUIBuilder WithMainThreadScheduler(this IReactiveUIBuilder builder, IScheduler scheduler, bool setRxApp = true) + public static IReactiveUIBuilder WithMainThreadScheduler( + this IReactiveUIBuilder builder, + IScheduler scheduler, + bool setRxApp) { ArgumentExceptionHelper.ThrowIfNull(builder); @@ -170,7 +216,9 @@ public static IReactiveUIBuilder WithMainThreadScheduler(this IReactiveUIBuilder /// The builder instance for chaining. /// /// builder. - public static IReactiveUIBuilder WithRegistrationOnBuild(this IReactiveUIBuilder builder, Action configureAction) + public static IReactiveUIBuilder WithRegistrationOnBuild( + this IReactiveUIBuilder builder, + Action configureAction) { ArgumentExceptionHelper.ThrowIfNull(builder); @@ -187,7 +235,9 @@ public static IReactiveUIBuilder WithRegistrationOnBuild(this IReactiveUIBuilder /// The builder instance for chaining. /// /// builder. - public static IReactiveUIBuilder WithRegistration(this IReactiveUIBuilder builder, Action configureAction) + public static IReactiveUIBuilder WithRegistration( + this IReactiveUIBuilder builder, + Action configureAction) { ArgumentExceptionHelper.ThrowIfNull(builder); @@ -204,7 +254,8 @@ public static IReactiveUIBuilder WithRegistration(this IReactiveUIBuilder builde /// The builder instance for chaining. /// /// builder. - [RequiresUnreferencedCode("Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to RegisterView explicitly.")] + [RequiresUnreferencedCode( + "Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to RegisterView explicitly.")] public static IReactiveUIBuilder WithViewsFromAssembly(this IReactiveUIBuilder builder, Assembly assembly) { ArgumentExceptionHelper.ThrowIfNull(builder); @@ -222,6 +273,10 @@ public static IReactiveUIBuilder WithViewsFromAssembly(this IReactiveUIBuilder b /// The builder instance for method chaining. /// /// builder. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IReactiveUIBuilder WithPlatformModule(this IReactiveUIBuilder builder) where T : IWantsToRegisterStuff, new() { @@ -253,23 +308,25 @@ public static IReactiveUIBuilder UsingSplatModule(this IReactiveUIBuilder bui /// /// Uses the splat builder. /// - /// The reactive UI builder. + /// The reactive UI builder. /// The application builder. /// /// The builder instance for method chaining. /// - public static IReactiveUIBuilder UsingSplatBuilder(this IReactiveUIBuilder reactiveUIBuilder, Action? appBuilder) + public static IReactiveUIBuilder UsingSplatBuilder( + this IReactiveUIBuilder reactiveUiBuilder, + Action? appBuilder) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - appBuilder?.Invoke(reactiveUIBuilder); - return reactiveUIBuilder; + appBuilder?.Invoke(reactiveUiBuilder); + return reactiveUiBuilder; } /// /// Configures a custom platform implementation for ReactiveUI. /// - /// The reactive UI builder. + /// The reactive UI builder. /// The main thread scheduler for the platform. /// The platform-specific service registrations. /// @@ -277,135 +334,147 @@ public static IReactiveUIBuilder UsingSplatBuilder(this IReactiveUIBuilder react /// /// reactiveUIBuilder. public static IReactiveUIBuilder ForCustomPlatform( - this IReactiveUIBuilder reactiveUIBuilder, + this IReactiveUIBuilder reactiveUiBuilder, IScheduler mainThreadScheduler, Action platformServices) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder + reactiveUiBuilder .WithMainThreadScheduler(mainThreadScheduler) .WithRegistration(platformServices); - return reactiveUIBuilder; + return reactiveUiBuilder; } /// /// Configures ReactiveUI for multiple platforms simultaneously. /// - /// The reactive UI builder. + /// The reactive UI builder. /// The platform configuration actions. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder ForPlatforms(this IReactiveUIBuilder reactiveUIBuilder, params Action[] platformConfigurations) + public static IReactiveUIBuilder ForPlatforms( + this IReactiveUIBuilder reactiveUiBuilder, + params Action[] platformConfigurations) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.ForPlatforms(platformConfigurations); - return reactiveUIBuilder; + reactiveUiBuilder.ForPlatforms(platformConfigurations); + return reactiveUiBuilder; } /// /// Configures the ReactiveUI message bus. /// - /// The reactive UI builder. + /// The reactive UI builder. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder WithMessageBus(this IReactiveUIBuilder reactiveUIBuilder) + public static IReactiveUIBuilder WithMessageBus(this IReactiveUIBuilder reactiveUiBuilder) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.WithMessageBus(); - return reactiveUIBuilder; + reactiveUiBuilder.WithMessageBus(); + return reactiveUiBuilder; } /// /// Configures the ReactiveUI message bus. /// - /// The reactive UI builder. + /// The reactive UI builder. /// The configuration action. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder WithMessageBus(this IReactiveUIBuilder reactiveUIBuilder, Action configure) + public static IReactiveUIBuilder WithMessageBus( + this IReactiveUIBuilder reactiveUiBuilder, + Action configure) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.WithMessageBus(configure); - return reactiveUIBuilder; + reactiveUiBuilder.WithMessageBus(configure); + return reactiveUiBuilder; } /// /// Registers a custom message bus instance. /// - /// The reactive UI builder. + /// The reactive UI builder. /// The message bus instance to use. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder WithMessageBus(this IReactiveUIBuilder reactiveUIBuilder, IMessageBus messageBus) + public static IReactiveUIBuilder WithMessageBus(this IReactiveUIBuilder reactiveUiBuilder, IMessageBus messageBus) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.WithMessageBus(messageBus); - return reactiveUIBuilder; + reactiveUiBuilder.WithMessageBus(messageBus); + return reactiveUiBuilder; } /// /// Configures the ReactiveUI view locator. /// - /// The reactive UI builder. + /// The reactive UI builder. /// The configuration action. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder ConfigureViewLocator(this IReactiveUIBuilder reactiveUIBuilder, Action configure) + public static IReactiveUIBuilder ConfigureViewLocator( + this IReactiveUIBuilder reactiveUiBuilder, + Action configure) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.ConfigureViewLocator(configure); - return reactiveUIBuilder; + reactiveUiBuilder.ConfigureViewLocator(configure); + return reactiveUiBuilder; } /// /// Configures the ReactiveUI suspension driver. /// - /// The reactive UI builder. + /// The reactive UI builder. /// The configuration action. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder ConfigureSuspensionDriver(this IReactiveUIBuilder reactiveUIBuilder, Action configure) + public static IReactiveUIBuilder ConfigureSuspensionDriver( + this IReactiveUIBuilder reactiveUiBuilder, + Action configure) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.ConfigureSuspensionDriver(configure); - return reactiveUIBuilder; + reactiveUiBuilder.ConfigureSuspensionDriver(configure); + return reactiveUiBuilder; } /// /// Registers a custom view model with the dependency resolver. /// /// The view model type. - /// The reactive UI builder. + /// The reactive UI builder. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder RegisterViewModel(this IReactiveUIBuilder reactiveUIBuilder) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IReactiveUIBuilder RegisterViewModel(this IReactiveUIBuilder reactiveUiBuilder) where TViewModel : class, IReactiveObject, new() { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.RegisterViewModel(); - return reactiveUIBuilder; + reactiveUiBuilder.RegisterViewModel(); + return reactiveUiBuilder; } /// @@ -413,37 +482,48 @@ public static IReactiveUIBuilder RegisterViewModel(this IReactiveUIB /// /// The type of the view model to register. Must be a class that implements IReactiveObject and has a parameterless /// constructor. - /// The ReactiveUI builder to configure. Cannot be null. + /// The ReactiveUI builder to configure. Cannot be null. /// The same ReactiveUI builder instance, to allow for method chaining. - public static IReactiveUIBuilder RegisterConstantViewModel(this IReactiveUIBuilder reactiveUIBuilder) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IReactiveUIBuilder RegisterConstantViewModel(this IReactiveUIBuilder reactiveUiBuilder) where TViewModel : class, IReactiveObject, new() { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.RegisterConstantViewModel(); - return reactiveUIBuilder; + reactiveUiBuilder.RegisterConstantViewModel(); + return reactiveUiBuilder; } /// /// Registers a custom view model with the dependency resolver. /// /// The view model type. - /// The reactive UI builder. + /// The reactive UI builder. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] #if NET6_0_OR_GREATER - public static IReactiveUIBuilder RegisterSingletonViewModel<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TViewModel>(this IReactiveUIBuilder reactiveUIBuilder) + public static IReactiveUIBuilder RegisterSingletonViewModel< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + TViewModel>( + this IReactiveUIBuilder reactiveUiBuilder) #else - public static IReactiveUIBuilder RegisterSingletonViewModel(this IReactiveUIBuilder reactiveUIBuilder) + public static IReactiveUIBuilder RegisterSingletonViewModel(this IReactiveUIBuilder reactiveUiBuilder) #endif where TViewModel : class, IReactiveObject, new() { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.RegisterSingletonViewModel(); - return reactiveUIBuilder; + reactiveUiBuilder.RegisterSingletonViewModel(); + return reactiveUiBuilder; } /// @@ -451,19 +531,23 @@ public static IReactiveUIBuilder RegisterSingletonViewModel(this IRe /// /// The view type. /// The view model type. - /// The reactive UI builder. + /// The reactive UI builder. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder RegisterView(this IReactiveUIBuilder reactiveUIBuilder) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IReactiveUIBuilder RegisterView(this IReactiveUIBuilder reactiveUiBuilder) where TView : class, IViewFor, new() where TViewModel : class, IReactiveObject { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.RegisterView(); - return reactiveUIBuilder; + reactiveUiBuilder.RegisterView(); + return reactiveUiBuilder; } /// @@ -471,42 +555,46 @@ public static IReactiveUIBuilder RegisterView(this IReactiveU /// /// The view type. /// The view model type. - /// The reactive UI builder. + /// The reactive UI builder. /// /// The builder instance for chaining. /// /// reactiveUIBuilder. - public static IReactiveUIBuilder RegisterSingletonView(this IReactiveUIBuilder reactiveUIBuilder) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IReactiveUIBuilder RegisterSingletonView(this IReactiveUIBuilder reactiveUiBuilder) where TView : class, IViewFor, new() where TViewModel : class, IReactiveObject { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIBuilder); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiBuilder); - reactiveUIBuilder.RegisterSingletonView(); - return reactiveUIBuilder; + reactiveUiBuilder.RegisterSingletonView(); + return reactiveUiBuilder; } /// /// Resolves a single instance and passes it to the action. /// /// The type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUiInstance, Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - action?.Invoke(reactiveUIInstance.Current.GetService()); - return reactiveUIInstance; + action?.Invoke(reactiveUiInstance.Current.GetService()); + return reactiveUiInstance; } /// @@ -514,28 +602,27 @@ public static IReactiveUIInstance WithInstance(this IReactiveUIInstance react /// /// The first type to resolve. /// The second type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action(current.GetService(), current.GetService()); - } + var current = reactiveUiInstance.Current; + action(current.GetService(), current.GetService()); - return reactiveUIInstance; + return reactiveUiInstance; } /// @@ -544,28 +631,27 @@ public static IReactiveUIInstance WithInstance(this IReactiveUIInstance /// The first type to resolve. /// The second type to resolve. /// The third type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action(current.GetService(), current.GetService(), current.GetService()); - } + var current = reactiveUiInstance.Current; + action(current.GetService(), current.GetService(), current.GetService()); - return reactiveUIInstance; + return reactiveUiInstance; } /// @@ -575,28 +661,31 @@ public static IReactiveUIInstance WithInstance(this IReactiveUIInsta /// The second type to resolve. /// The third type to resolve. /// The fourth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action(current.GetService(), current.GetService(), current.GetService(), current.GetService()); - } + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); - return reactiveUIInstance; + return reactiveUiInstance; } /// @@ -607,28 +696,32 @@ public static IReactiveUIInstance WithInstance(this IReactiveUII /// The third type to resolve. /// The fourth type to resolve. /// The fifth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action(current.GetService(), current.GetService(), current.GetService(), current.GetService(), current.GetService()); - } + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); - return reactiveUIInstance; + return reactiveUiInstance; } /// @@ -640,34 +733,33 @@ public static IReactiveUIInstance WithInstance(this IReactiv /// The fourth type to resolve. /// The fifth type to resolve. /// The sixth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); - } + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); - return reactiveUIInstance; + return reactiveUiInstance; } /// @@ -680,35 +772,34 @@ public static IReactiveUIInstance WithInstance(this IRea /// The fifth type to resolve. /// The sixth type to resolve. /// The seventh type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); - - if (reactiveUIInstance.Current is null) - { - return reactiveUIInstance; - } + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (action is not null) + if (reactiveUiInstance.Current is null || action is null) { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + return reactiveUiInstance; } - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -722,36 +813,35 @@ public static IReactiveUIInstance WithInstance(this /// The sixth type to resolve. /// The seventh type to resolve. /// The eighth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); - - if (reactiveUIInstance.Current is null) - { - return reactiveUIInstance; - } + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (action is not null) + if (reactiveUiInstance.Current is null || action is null) { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + return reactiveUiInstance; } - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -766,37 +856,36 @@ public static IReactiveUIInstance WithInstance(t /// The seventh type to resolve. /// The eighth type to resolve. /// The ninth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); - } - - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -812,38 +901,37 @@ public static IReactiveUIInstance WithInstanceThe eighth type to resolve. /// The ninth type to resolve. /// The tenth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); - } - - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -860,39 +948,38 @@ public static IReactiveUIInstance WithInstanceThe ninth type to resolve. /// The tenth type to resolve. /// The eleventh type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); - } - - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -910,40 +997,39 @@ public static IReactiveUIInstance WithInstanceThe tenth type to resolve. /// The eleventh type to resolve. /// The twelfth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); - - if (reactiveUIInstance.Current is null) - { - return reactiveUIInstance; - } + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (action is not null) + if (reactiveUiInstance.Current is null || action is null) { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + return reactiveUiInstance; } - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -962,41 +1048,40 @@ public static IReactiveUIInstance WithInstanceThe eleventh type to resolve. /// The twelfth type to resolve. /// The thirteenth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); - - if (reactiveUIInstance.Current is null) - { - return reactiveUIInstance; - } + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (action is not null) + if (reactiveUiInstance.Current is null || action is null) { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + return reactiveUiInstance; } - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -1016,42 +1101,41 @@ public static IReactiveUIInstance WithInstanceThe twelfth type to resolve. /// The thirteenth type to resolve. /// The fourteenth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); - } - - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -1072,43 +1156,42 @@ public static IReactiveUIInstance WithInstanceThe thirteenth type to resolve. /// The fourteenth type to resolve. /// The fifteenth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (reactiveUIInstance.Current is null) + if (reactiveUiInstance.Current is null || action is null) { - return reactiveUIInstance; + return reactiveUiInstance; } - if (action is not null) - { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); - } - - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// @@ -1130,44 +1213,44 @@ public static IReactiveUIInstance WithInstanceThe fourteenth type to resolve. /// The fifteenth type to resolve. /// The sixteenth type to resolve. - /// The reactive UI instance. + /// The reactive UI instance. /// The action. /// /// IReactiveUIInstance instance for chaining. /// /// reactiveUIInstance. - public static IReactiveUIInstance WithInstance(this IReactiveUIInstance reactiveUIInstance, Action action) + public static IReactiveUIInstance + WithInstance( + this IReactiveUIInstance reactiveUiInstance, + Action action) { - ArgumentExceptionHelper.ThrowIfNull(reactiveUIInstance); - - if (reactiveUIInstance.Current == null) - { - return reactiveUIInstance; - } + ArgumentExceptionHelper.ThrowIfNull(reactiveUiInstance); - if (action is not null) + if (reactiveUiInstance.Current == null || action is null) { - var current = reactiveUIInstance.Current; - action( - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService(), - current.GetService()); + return reactiveUiInstance; } - return reactiveUIInstance; + var current = reactiveUiInstance.Current; + action( + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService(), + current.GetService()); + + return reactiveUiInstance; } /// diff --git a/src/ReactiveUI/Mixins/ChangeSetMixin.cs b/src/ReactiveUI/Mixins/ChangeSetMixin.cs index a3f56bbfc6..965531d45c 100644 --- a/src/ReactiveUI/Mixins/ChangeSetMixin.cs +++ b/src/ReactiveUI/Mixins/ChangeSetMixin.cs @@ -1,23 +1,21 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using DynamicData; - namespace ReactiveUI; /// -/// Mixin associated with the DynamicData IChangeSet class. +/// Mixin associated with the change-set types. /// public static class ChangeSetMixin { /// /// Is the change set associated with a count change. /// - /// The change list to evaluate. + /// The change set to evaluate. /// If the change set is caused by the count being changed. - public static bool HasCountChanged(this IChangeSet changeSet) // TODO: Create Test + public static bool HasCountChanged(this IReactiveChangeSet changeSet) { ArgumentExceptionHelper.ThrowIfNull(changeSet); @@ -25,19 +23,50 @@ public static bool HasCountChanged(this IChangeSet changeSet) // TODO: Create Te } /// - /// Is the change set associated with a count change. + /// Filters a change-set stream to only those sets that change the collection count. /// - /// The change list to evaluate. - /// An observable of changes that only have count changes. - public static IObservable CountChanged(this IObservable changeSet) => changeSet.Where(HasCountChanged); // TODO: Create Test + /// The change set item type. + /// The change-set stream to evaluate. + /// An observable of change sets that only have count changes. + public static IObservable> CountChanged(this IObservable> changeSet) + { + ArgumentExceptionHelper.ThrowIfNull(changeSet); - /// - /// Is the change set associated with a count change. - /// - /// The change set type. - /// The change list to evaluate. - /// An observable of changes that only have count changes. - public static IObservable> CountChanged(this IObservable> changeSet) - where T : notnull => - changeSet.Where(static x => x.HasCountChanged()); // TODO: Create Test + return new CountChangedObservable(changeSet); + } + + /// Forwards only the change sets that alter the collection count. + /// The change set item type. + /// The source change-set stream. + private sealed class CountChangedObservable(IObservable> source) : IObservable> + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// Gates the source change sets on a count change. + /// The observer receiving count-changing sets. + private sealed class Sink(IObserver> downstream) : IObserver> + { + /// + public void OnNext(IReactiveChangeSet value) + { + if (value.Adds <= 0 && value.Removes <= 0) + { + return; + } + + downstream.OnNext(value); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } } diff --git a/src/ReactiveUI/Mixins/CompatMixins.cs b/src/ReactiveUI/Mixins/CompatMixins.cs index fdde665aba..9c27b16836 100644 --- a/src/ReactiveUI/Mixins/CompatMixins.cs +++ b/src/ReactiveUI/Mixins/CompatMixins.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Mixins/DependencyResolverMixins.cs b/src/ReactiveUI/Mixins/DependencyResolverMixins.cs index 2b3a1d5af0..a9df994188 100644 --- a/src/ReactiveUI/Mixins/DependencyResolverMixins.cs +++ b/src/ReactiveUI/Mixins/DependencyResolverMixins.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,34 +20,33 @@ public static class DependencyResolverMixins /// /// The dependency injection resolver to register the Views with. /// The assembly to search using reflection for IViewFor classes. - [RequiresUnreferencedCode("Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to register views explicitly.")] + [RequiresUnreferencedCode( + "Scans assembly for IViewFor implementations using reflection. For AOT compatibility, use the ReactiveUIBuilder pattern to register views explicitly.")] public static void RegisterViewsForViewModels(this IMutableDependencyResolver resolver, Assembly assembly) { ArgumentExceptionHelper.ThrowIfNull(resolver); ArgumentExceptionHelper.ThrowIfNull(assembly); - // for each type that implements IViewFor foreach (var ti in assembly.DefinedTypes - .Where(static ti => ti.ImplementedInterfaces.Contains(typeof(IViewFor)) && !ti.IsAbstract)) + .Where(static ti => ti.ImplementedInterfaces.Contains(typeof(IViewFor)) && !ti.IsAbstract)) { - // Skip types explicitly marked to be excluded from auto view registration if (ti.GetCustomAttribute() is not null) { continue; } - // grab the first _implemented_ interface that also implements IViewFor, this should be the expected IViewFor<>` - var ivf = ti.ImplementedInterfaces.FirstOrDefault(static t => t.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IViewFor))); + var ivf = ti.ImplementedInterfaces.FirstOrDefault(static t => + t.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IViewFor))); - // need to check for null because some classes may implement IViewFor but not IViewFor - we don't care about those - if (ivf is not null) + if (ivf is null) { - // my kingdom for c# 6! - var contractSource = ti.GetCustomAttribute(); - var contract = contractSource?.Contract; - - RegisterType(resolver, ti, ivf, contract); + continue; } + + var contractSource = ti.GetCustomAttribute(); + var contract = contractSource?.Contract; + + RegisterType(resolver, ti, ivf, contract); } } @@ -67,30 +66,41 @@ public static void RegisterViewsForViewModels(this IMutableDependencyResolver re /// the registration is not associated with a contract. private static void RegisterType( IMutableDependencyResolver resolver, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | + DynamicallyAccessedMemberTypes.NonPublicConstructors)] TypeInfo ti, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type serviceType, string? contract) { var factory = TypeFactory(ti); - var isSingleton = ti.GetCustomAttribute() is not null; - - if (isSingleton && contract is not null) - { - resolver.RegisterLazySingleton(factory, serviceType, contract); - } - else if (isSingleton) + switch (ti.GetCustomAttribute() is not null) { - resolver.RegisterLazySingleton(factory, serviceType); - } - else if (contract is not null) - { - resolver.Register(factory, serviceType, contract); - } - else - { - resolver.Register(factory, serviceType); + case true when contract is not null: + { + resolver.RegisterLazySingleton(factory, serviceType, contract); + break; + } + + case true: + { + resolver.RegisterLazySingleton(factory, serviceType); + break; + } + + default: + { + if (contract is not null) + { + resolver.Register(factory, serviceType, contract); + } + else + { + resolver.Register(factory, serviceType); + } + + break; + } } } @@ -102,13 +112,17 @@ private static void RegisterType( /// A delegate that creates a new instance of the specified type when invoked. /// Thrown if the specified type does not have a public parameterless constructor, or if instantiation fails. private static Func TypeFactory( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | + DynamicallyAccessedMemberTypes.NonPublicConstructors)] TypeInfo typeInfo) { - var parameterlessConstructor = typeInfo.DeclaredConstructors.FirstOrDefault(ci => ci.IsPublic && ci.GetParameters().Length == 0); + var parameterlessConstructor = + typeInfo.DeclaredConstructors.FirstOrDefault(ci => ci.IsPublic && ci.GetParameters().Length == 0); return parameterlessConstructor is null - ? throw new Exception($"Failed to register type {typeInfo.FullName} because it's missing a parameterless constructor.") + ? throw new InvalidOperationException( + $"Failed to register type {typeInfo.FullName} because it's missing a parameterless constructor.") : () => Activator.CreateInstance(typeInfo.AsType()) - ?? throw new Exception($"Failed to instantiate type {typeInfo.FullName} - ensure it has a public parameterless constructor."); + ?? throw new InvalidOperationException( + $"Failed to instantiate type {typeInfo.FullName} - ensure it has a public parameterless constructor."); } } diff --git a/src/ReactiveUI/Mixins/ExpressionMixins.cs b/src/ReactiveUI/Mixins/ExpressionMixins.cs index 74ca438943..ee4a5c6bbf 100644 --- a/src/ReactiveUI/Mixins/ExpressionMixins.cs +++ b/src/ReactiveUI/Mixins/ExpressionMixins.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; using System.Reflection; using System.Text; @@ -21,7 +22,7 @@ public static class ExpressionMixins /// An enumerable of expressions. public static IEnumerable GetExpressionChain(this Expression expression) { - var expressions = new List(); + List expressions = []; var node = expression; while (node is not null && node.NodeType != ExpressionType.Parameter) @@ -31,15 +32,12 @@ public static IEnumerable GetExpressionChain(this Expression express case ExpressionType.Index when node is IndexExpression indexExpression: { var parent = indexExpression.GetParent(); - if (indexExpression.Object is not null && parent is not null && indexExpression.Object.NodeType != ExpressionType.Parameter) - { - expressions.Add( - indexExpression.Update(Expression.Parameter(parent.Type), indexExpression.Arguments)); - } - else - { - expressions.Add(indexExpression); - } + expressions.Add( + indexExpression.Object is not null && + parent is not null && + indexExpression.Object.NodeType != ExpressionType.Parameter + ? indexExpression.Update(Expression.Parameter(parent.Type), indexExpression.Arguments) + : indexExpression); node = indexExpression.Object; break; @@ -48,14 +46,8 @@ public static IEnumerable GetExpressionChain(this Expression express case ExpressionType.MemberAccess when node is MemberExpression memberExpression: { var parent = memberExpression.GetParent(); - if (parent is not null && memberExpression.Expression is not null && memberExpression.Expression.NodeType != ExpressionType.Parameter) - { - expressions.Add(memberExpression.Update(Expression.Parameter(parent.Type))); - } - else - { - expressions.Add(memberExpression); - } + expressions.Add(parent is not null && memberExpression.Expression is not null && + memberExpression.Expression.NodeType != ExpressionType.Parameter ? memberExpression.Update(Expression.Parameter(parent.Type)) : memberExpression); node = memberExpression.Expression; break; @@ -63,7 +55,7 @@ public static IEnumerable GetExpressionChain(this Expression express default: { - var errorMessageBuilder = new StringBuilder($"Unsupported expression of type '{node.NodeType}'."); + StringBuilder errorMessageBuilder = new($"Unsupported expression of type '{node.NodeType}'."); if (node is ConstantExpression) { @@ -94,15 +86,22 @@ public static IEnumerable GetExpressionChain(this Expression express switch (expression.NodeType) { case ExpressionType.Index when expression is IndexExpression indexExpression: - info = indexExpression.Indexer; - break; + { + info = indexExpression.Indexer; + break; + } + case ExpressionType.MemberAccess when expression is MemberExpression memberExpression: - info = memberExpression.Member; - break; - case ExpressionType.Convert or ExpressionType.ConvertChecked when expression is UnaryExpression unaryExpression: + { + info = memberExpression.Member; + break; + } + + case ExpressionType.Convert or ExpressionType.ConvertChecked + when expression is UnaryExpression unaryExpression: return GetMemberInfo(unaryExpression.Operand); default: - throw new NotSupportedException($"Unsupported expression type: '{expression.NodeType}'"); + throw new NotSupportedException($"Unsupported {nameof(expression)} type: '{expression.NodeType}'"); } return info; @@ -113,7 +112,7 @@ public static IEnumerable GetExpressionChain(this Expression express /// /// The expression. /// The parent expression. - public static Expression? GetParent(this Expression expression) // TODO: Create Test + public static Expression? GetParent(this Expression expression) { ArgumentExceptionHelper.ThrowIfNull(expression); @@ -132,15 +131,15 @@ public static IEnumerable GetExpressionChain(this Expression express /// /// The expression. /// An array of arguments. - public static object?[]? GetArgumentsArray(this Expression expression) // TODO: Create Test + public static object?[]? GetArgumentsArray(this Expression expression) { ArgumentExceptionHelper.ThrowIfNull(expression); - if (expression.NodeType == ExpressionType.Index) + if (expression.NodeType != ExpressionType.Index) { - return ((IndexExpression)expression).Arguments.Cast().Select(static c => c.Value).ToArray(); + return null; } - return null; + return [.. ((IndexExpression)expression).Arguments.Cast().Select(static c => c.Value)]; } } diff --git a/src/ReactiveUI/Mixins/MutableDependencyResolverAOTExtensions.cs b/src/ReactiveUI/Mixins/MutableDependencyResolverAOTExtensions.cs index 73e49b4a4f..39799a0a64 100644 --- a/src/ReactiveUI/Mixins/MutableDependencyResolverAOTExtensions.cs +++ b/src/ReactiveUI/Mixins/MutableDependencyResolverAOTExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -11,6 +11,9 @@ namespace ReactiveUI; /// AOT-friendly generic registration helpers for IMutableDependencyResolver. /// These avoid reflection by relying on generic constraints and parameterless constructors. /// +[SuppressMessage("Minor Code Smell", "S100:Methods and properties should be named in PascalCase", Justification = "This is a legacy method name.")] +[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This is a legacy method name.")] +[SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "This is a legacy method name.")] internal static class MutableDependencyResolverAOTExtensions { /// @@ -31,7 +34,13 @@ internal static class MutableDependencyResolverAOTExtensions /// An optional contract string to distinguish this registration from others. If null, the registration is made /// without a contract. /// The dependency resolver instance, enabling method chaining. - internal static IMutableDependencyResolver RegisterViewForViewModelAOT(this IMutableDependencyResolver resolver, string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + internal static IMutableDependencyResolver RegisterViewForViewModelAOT( + this IMutableDependencyResolver resolver, + string? contract = null) where TView : class, IViewFor, new() where TViewModel : class { @@ -61,7 +70,13 @@ internal static IMutableDependencyResolver RegisterViewForViewModelAOTAn optional contract string to associate with the registration. If null, the registration is made without a /// contract. /// The dependency resolver instance, enabling method chaining. - internal static IMutableDependencyResolver RegisterSingletonViewForViewModelAOT(this IMutableDependencyResolver resolver, string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + internal static IMutableDependencyResolver RegisterSingletonViewForViewModelAOT( + this IMutableDependencyResolver resolver, + string? contract = null) where TView : class, IViewFor, new() where TViewModel : class { diff --git a/src/ReactiveUI/Mixins/MutableDependencyResolverExtensions.cs b/src/ReactiveUI/Mixins/MutableDependencyResolverExtensions.cs index de05853976..f7546cd5e0 100644 --- a/src/ReactiveUI/Mixins/MutableDependencyResolverExtensions.cs +++ b/src/ReactiveUI/Mixins/MutableDependencyResolverExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -18,6 +18,23 @@ public static class MutableDependencyResolverExtensions /// static MutableDependencyResolverExtensions() => RxAppBuilder.EnsureInitialized(); + /// + /// Registers a view type for a specified view model type with the dependency resolver. + /// + /// The view type to register. Must implement IViewFor{TViewModel} and have a parameterless constructor. + /// The view model type for which the view is registered. + /// The dependency resolver to which the view registration is added. Cannot be null. + /// The dependency resolver instance, enabling method chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IMutableDependencyResolver RegisterViewForViewModel( + this IMutableDependencyResolver resolver) + where TView : class, IViewFor, new() + where TViewModel : class => + RegisterViewForViewModel(resolver, null); + /// /// Registers a view type for a specified view model type with the dependency resolver, optionally using a contract. /// @@ -29,7 +46,13 @@ public static class MutableDependencyResolverExtensions /// The dependency resolver to which the view registration is added. Cannot be null. /// An optional contract to associate with the registration. If null, the registration is made without a contract. /// The dependency resolver instance, enabling method chaining. - public static IMutableDependencyResolver RegisterViewForViewModel(this IMutableDependencyResolver resolver, string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IMutableDependencyResolver RegisterViewForViewModel( + this IMutableDependencyResolver resolver, + string? contract) where TView : class, IViewFor, new() where TViewModel : class { @@ -46,6 +69,23 @@ public static IMutableDependencyResolver RegisterViewForViewModel + /// Registers a singleton view implementation for the specified view model type in the dependency resolver. + /// + /// The type of the view to register. Must implement IViewFor{TViewModel} and have a parameterless constructor. + /// The type of the view model associated with the view. + /// The dependency resolver in which to register the singleton view. + /// The dependency resolver instance, enabling method chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IMutableDependencyResolver RegisterSingletonViewForViewModel( + this IMutableDependencyResolver resolver) + where TView : class, IViewFor, new() + where TViewModel : class => + RegisterSingletonViewForViewModel(resolver, null); + /// /// Registers a singleton view implementation for the specified view model type in the dependency resolver. /// @@ -58,7 +98,13 @@ public static IMutableDependencyResolver RegisterViewForViewModelAn optional contract string to associate with the registration. If null, the registration is made without a /// contract. /// The dependency resolver instance, enabling method chaining. - public static IMutableDependencyResolver RegisterSingletonViewForViewModel(this IMutableDependencyResolver resolver, string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IMutableDependencyResolver RegisterSingletonViewForViewModel( + this IMutableDependencyResolver resolver, + string? contract) where TView : class, IViewFor, new() where TViewModel : class { diff --git a/src/ReactiveUI/Mixins/ObservableLoggingMixin.cs b/src/ReactiveUI/Mixins/ObservableLoggingMixin.cs index 505b72541c..51c9165288 100644 --- a/src/ReactiveUI/Mixins/ObservableLoggingMixin.cs +++ b/src/ReactiveUI/Mixins/ObservableLoggingMixin.cs @@ -1,11 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Globalization; +using System.Reactive.Linq; using ReactiveUI.Builder; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -19,6 +21,38 @@ public static class ObservableLoggingMixin /// static ObservableLoggingMixin() => RxAppBuilder.EnsureInitialized(); + /// + /// Returns an observable sequence that logs each notification using the specified logger object. + /// + /// The type of the elements in the source observable sequence. + /// The type of the logger object. Must implement IEnableLogger. + /// The source observable sequence whose notifications will be logged. + /// An object that provides logging capabilities. + /// An observable sequence that logs each notification using the provided logger. + public static IObservable Log( + this IObservable @this, + TObj logObject) + where TObj : IEnableLogger + => + Log(@this, logObject, null, null); + + /// + /// Returns an observable sequence that logs each notification using the specified logger object and message. + /// + /// The type of the elements in the source observable sequence. + /// The type of the logger object. Must implement IEnableLogger. + /// The source observable sequence whose notifications will be logged. + /// An object that provides logging capabilities. + /// An optional message to include in each log entry. If null, an empty string is used. + /// An observable sequence that logs each notification using the provided logger. + public static IObservable Log( + this IObservable @this, + TObj logObject, + string? message) + where TObj : IEnableLogger + => + Log(@this, logObject, message, null); + /// /// Returns an observable sequence that logs each notification using the specified logger object. /// @@ -26,37 +60,63 @@ public static class ObservableLoggingMixin /// effects for logging purposes. Logging occurs for each notification: OnNext (with the element value), OnError, /// and OnCompleted. The returned observable can be further composed or subscribed to as usual. /// The type of the elements in the source observable sequence. - /// The type of the logger object. Must implement . + /// The type of the logger object. Must implement IEnableLogger. /// The source observable sequence whose notifications will be logged. - /// An object that provides logging capabilities. Must implement . + /// An object that provides logging capabilities. /// An optional message to include in each log entry. If null, an empty string is used. - /// An optional function to convert each element to a string for logging. If null, the element's method is used. + /// An optional function to convert each element to a string for logging. If null, the element's ToString method is used. /// An observable sequence that is functionally equivalent to the source, but logs each OnNext, OnError, and /// OnCompleted notification using the provided logger. public static IObservable Log( this IObservable @this, TObj logObject, - string? message = null, - Func? stringifier = null) // TODO: Create Test + string? message, + Func? stringifier) where TObj : IEnableLogger { message ??= string.Empty; - if (stringifier is not null) - { - return @this.Do( - x => logObject.Log().Info(CultureInfo.InvariantCulture, "{0} OnNext: {1}", message, stringifier(x)), - ex => logObject.Log().Warn(ex, message + " OnError"), - () => logObject.Log().Info(CultureInfo.InvariantCulture, "{0} OnCompleted", message)); - } + Action onNext = stringifier is not null + ? x => logObject.Log().Info(CultureInfo.InvariantCulture, "{0} OnNext: {1}", message, stringifier(x)) + : x => logObject.Log().Info(CultureInfo.InvariantCulture, "{0} OnNext: {1}", message, x); - return @this.Do( - x => logObject.Log().Info(CultureInfo.InvariantCulture, "{0} OnNext: {1}", message, x), - ex => logObject.Log().Warn(ex, message + " OnError"), - () => logObject.Log().Info(CultureInfo.InvariantCulture, "{0} OnCompleted", message)); + return new LoggingTeeObservable( + @this, + onNext, + ex => logObject.Log().Warn(ex, message + " OnError"), + () => logObject.Log().Info(CultureInfo.InvariantCulture, "{0} OnCompleted", message)); } + /// + /// Returns an observable sequence that logs any exception and continues with a default empty sequence. + /// + /// The type of the elements in the observable sequence. + /// The type of the logger, which must implement IEnableLogger. + /// The source observable sequence to monitor for exceptions. + /// An object that provides logging capabilities. + /// An observable sequence that logs exceptions and continues with an empty sequence. + public static IObservable LoggedCatch( + this IObservable @this, + TObj @class) + where TObj : IEnableLogger => + LoggedCatch(@this, @class, null, null); + + /// + /// Returns an observable sequence that logs any exception and continues with the provided fallback sequence. + /// + /// The type of the elements in the observable sequence. + /// The type of the logger, which must implement IEnableLogger. + /// The source observable sequence to monitor for exceptions. + /// An object that provides logging capabilities. + /// An observable sequence to continue with after an exception is caught. + /// An observable sequence that logs exceptions and continues with the fallback sequence. + public static IObservable LoggedCatch( + this IObservable @this, + TObj @class, + IObservable? next) + where TObj : IEnableLogger => + LoggedCatch(@this, @class, next, null); + /// /// Returns an observable sequence that logs any exception using the specified logger and continues with the /// provided fallback sequence, if supplied. @@ -65,25 +125,47 @@ public static IObservable Log( /// optionally providing a fallback sequence to continue processing. The exception is logged at the warning level /// using the provided logger. /// The type of the elements in the observable sequence. - /// The type of the logger, which must implement . + /// The type of the logger, which must implement IEnableLogger. /// The source observable sequence to monitor for exceptions. /// An object that provides logging capabilities and is used to log any exceptions encountered. - /// An optional observable sequence to continue with after an exception is caught. If not specified, a default empty - /// sequence is used. + /// An observable sequence to continue with after an exception is caught. If null, a default empty sequence is used. /// An optional message to include in the log entry when an exception is caught. If null, an empty string is used. /// An observable sequence that emits the original elements until an exception occurs, logs the exception, and then /// continues with the specified fallback sequence. - public static IObservable LoggedCatch(this IObservable @this, TObj @class, IObservable? next = null, string? message = null) // TODO: Create Test + public static IObservable LoggedCatch( + this IObservable @this, + TObj @class, + IObservable? next, + string? message) where TObj : IEnableLogger { next ??= Observable.Default; - return @this.Catch(ex => + return new LoggedCatchObservable(@this, ex => { @class.Log().Warn(ex, message ?? string.Empty); return next; }); } + /// + /// Handles exceptions of a specified type in the observable sequence by logging a warning and continuing with an + /// alternative observable sequence. + /// + /// The type of the elements in the source observable sequence. + /// The type of the logger-enabled object used for logging. Must implement IEnableLogger. + /// The type of exception to catch and handle. Must derive from Exception. + /// The source observable sequence to monitor for exceptions. + /// An object that provides logging capabilities. + /// A function that returns an alternative observable sequence for the caught exception. + /// An observable sequence that continues with the next function after logging the exception. + public static IObservable LoggedCatch( + this IObservable @this, + TObj @class, + Func> next) + where TObj : IEnableLogger + where TException : Exception => + LoggedCatch(@this, @class, next, null); + /// /// Handles exceptions of a specified type in the observable sequence by logging a warning and continuing with an /// alternative observable sequence. @@ -101,12 +183,168 @@ public static IObservable LoggedCatch(this IObservable @this, TOb /// An optional message to include in the warning log. If null, an empty string is used. /// An observable sequence that continues with the sequence returned by the next function after logging the /// exception, or propagates other exceptions. - public static IObservable LoggedCatch(this IObservable @this, TObj @class, Func> next, string? message = null) // TODO: Create Test + public static IObservable LoggedCatch( + this IObservable @this, + TObj @class, + Func> next, + string? message) where TObj : IEnableLogger where TException : Exception => - @this.Catch(ex => + new LoggedCatchObservable(@this, ex => { @class.Log().Warn(ex, message ?? string.Empty); return next(ex); }); + + /// + /// A fused tee sink that invokes side-effect callbacks for each notification before forwarding it unchanged — + /// replacing the Do(onNext, onError, onCompleted) used by . + /// + /// The element type. + /// The source observable. + /// Invoked with each value before it is forwarded. + /// Invoked with the error before it is forwarded. + /// Invoked on completion before it is forwarded. + private sealed class LoggingTeeObservable( + IObservable source, + Action onNext, + Action onError, + Action onCompleted) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(onNext, onError, onCompleted, observer)); + } + + /// Runs each side-effect callback, then forwards the notification (forwarding the callback's own error if it throws). + private sealed class Sink(Action onNext, Action onError, Action onCompleted, IObserver downstream) + : IObserver + { + /// + public void OnNext(T value) + { + try + { + onNext(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(value); + } + + /// + public void OnError(Exception error) + { + try + { + onError(error); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnError(error); + } + + /// + public void OnCompleted() + { + try + { + onCompleted(); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnCompleted(); + } + } + } + + /// + /// A fused sink that forwards the source until an exception of type occurs, then + /// switches to the observable returned by — replacing the Catch<T, TException> + /// used by the LoggedCatch overloads. Exceptions of other types are propagated unchanged. + /// + /// The element type. + /// The exception type to catch. + /// The source observable. + /// Produces the continuation observable for a caught exception. + private sealed class LoggedCatchObservable( + IObservable source, + Func> handler) : IObservable + where TException : Exception + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(handler, observer); + sink.Run(source); + return sink; + } + + /// Forwards the source, switching to the handler's continuation on a matching exception. + private sealed class Sink(Func> handler, IObserver downstream) + : IObserver, IDisposable + { + /// The source subscription; disposed when switching to the continuation. + private readonly OnceDisposable _source = new(); + + /// The continuation subscription created after a caught exception. + private readonly MutableDisposable _continuation = new(); + + /// Begins observing the source. + /// The source observable. + public void Run(IObservable source) => _source.Disposable = source.Subscribe(this); + + /// + public void OnNext(T value) => downstream.OnNext(value); + + /// + public void OnCompleted() => downstream.OnCompleted(); + + /// + public void OnError(Exception error) + { + if (error is not TException typed) + { + downstream.OnError(error); + return; + } + + IObservable continuation; + try + { + continuation = handler(typed); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + _source.Dispose(); + _continuation.Disposable = continuation.Subscribe(downstream); + } + + /// + public void Dispose() + { + _source.Dispose(); + _continuation.Dispose(); + } + } + } } diff --git a/src/ReactiveUI/Mixins/ObservableMixins.cs b/src/ReactiveUI/Mixins/ObservableMixins.cs index c5dfa016a0..2c34f65e10 100644 --- a/src/ReactiveUI/Mixins/ObservableMixins.cs +++ b/src/ReactiveUI/Mixins/ObservableMixins.cs @@ -1,9 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; + using ReactiveUI.Builder; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -29,9 +32,7 @@ public static class ObservableMixins /// The observable that can contain nulls. /// A non nullable version of the observable that only emits valid values. public static IObservable WhereNotNull(this IObservable observable) => - observable - .Where(static x => x is not null) - .Select(static x => x!); + new WhereNotNullObservable(observable); /// /// Converts an asynchronous action into an observable sequence. Each subscription @@ -42,18 +43,19 @@ public static IObservable WhereNotNull(this IObservable observable) => /// Asynchronous action to convert. /// An observable sequence exposing a Unit value upon completion of the action, or an exception. internal static IObservable<(IObservable Result, Action Cancel)> FromAsyncWithAllNotifications( - Func actionAsync) => Observable.Defer( - () => - { - var cts = new CancellationTokenSource(); - var result = Observable.FromAsync( - async ctsBase => - { - using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ctsBase); - await actionAsync(linkedCts.Token); - }); - return Observable.Return<(IObservable Result, Action Cancel)>((result, () => cts.Cancel())); - }); + Func actionAsync) => + new DeferredValueObservable<(IObservable Result, Action Cancel)>(() => + { + var cts = new CancellationTokenSource(); + var result = new FromAsyncObservable( + async ct => + { + await actionAsync(ct).ConfigureAwait(false); + return Unit.Default; + }, + cts); + return (result, () => cts.Cancel()); + }); /// /// Converts an asynchronous action into an observable sequence. Each subscription @@ -66,19 +68,20 @@ public static IObservable WhereNotNull(this IObservable observable) => /// The parameter. /// An observable sequence exposing a Unit value upon completion of the action, or an exception. internal static IObservable<(IObservable Result, Action Cancel)> FromAsyncWithAllNotifications( - Func actionAsync, TParam param) => Observable.Defer( - () => - { - var cts = new CancellationTokenSource(); - var result = Observable.FromAsync( - async ctsBase => - { - using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ctsBase); - await actionAsync(param, linkedCts.Token); - }); - - return Observable.Return<(IObservable Result, Action Cancel)>((result, () => cts.Cancel())); - }); + Func actionAsync, + TParam param) => + new DeferredValueObservable<(IObservable Result, Action Cancel)>(() => + { + var cts = new CancellationTokenSource(); + var result = new FromAsyncObservable( + async ct => + { + await actionAsync(param, ct).ConfigureAwait(false); + return Unit.Default; + }, + cts); + return (result, () => cts.Cancel()); + }); /// /// Converts an asynchronous action into an observable sequence. Each subscription @@ -90,19 +93,13 @@ public static IObservable WhereNotNull(this IObservable observable) => /// Asynchronous action to convert. /// An observable sequence exposing a Unit value upon completion of the action, or an exception. internal static IObservable<(IObservable Result, Action Cancel)> FromAsyncWithAllNotifications( - Func> actionAsync) => Observable.Defer( - () => - { - var cts = new CancellationTokenSource(); - var result = Observable.FromAsync( - async ctsBase => - { - var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ctsBase); - return await actionAsync(linkedCts.Token); - }); - - return Observable.Return<(IObservable Result, Action Cancel)>((result, () => cts.Cancel())); - }); + Func> actionAsync) => + new DeferredValueObservable<(IObservable Result, Action Cancel)>(() => + { + var cts = new CancellationTokenSource(); + var result = new FromAsyncObservable(actionAsync, cts); + return (result, () => cts.Cancel()); + }); /// /// Converts an asynchronous action into an observable sequence. Each subscription @@ -115,18 +112,174 @@ public static IObservable WhereNotNull(this IObservable observable) => /// Asynchronous action to convert. /// The parameter. /// An observable sequence exposing a Unit value upon completion of the action, or an exception. - internal static IObservable<(IObservable Result, Action Cancel)> FromAsyncWithAllNotifications( - Func> actionAsync, TParam param) => Observable.Defer( - () => + internal static IObservable<(IObservable Result, Action Cancel)> FromAsyncWithAllNotifications< + TParam, + TResult>( + Func> actionAsync, + TParam param) => + new DeferredValueObservable<(IObservable Result, Action Cancel)>(() => + { + var cts = new CancellationTokenSource(); + var result = new FromAsyncObservable(ct => actionAsync(param, ct), cts); + return (result, () => cts.Cancel()); + }); + + /// Forwards only the non-null values of a source, projected to the non-nullable type. Replaces Where(x is not null).Select(x!). + /// The element type. + /// The source observable. + private sealed class WhereNotNullObservable(IObservable source) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// Forwards each non-null value as its non-nullable type. + /// The observer receiving non-null values. + private sealed class Sink(IObserver downstream) : IObserver + { + /// + public void OnNext(T? value) { - var cts = new CancellationTokenSource(); - var result = Observable.FromAsync( - async cancelFromRx => - { - using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancelFromRx); - return await actionAsync(param, linkedCts.Token); - }); + if (value is null) + { + return; + } + + downstream.OnNext(value); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// + /// Builds a value at subscription time and emits it once, then completes. Replaces Observable.Defer(() => Observable.Return(...)). + /// + /// The element type. + /// Builds the value to emit at subscription time. + private sealed class DeferredValueObservable(Func factory) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + T value; + try + { + value = factory(); + } + catch (Exception ex) + { + observer.OnError(ex); + return EmptyDisposable.Instance; + } + + observer.OnNext(value); + observer.OnCompleted(); + return EmptyDisposable.Instance; + } + } + + /// + /// Runs an asynchronous factory on subscription (with a cancellation token linked to the outer source and the + /// subscription) and emits its single result. Replaces Observable.FromAsync(...).Finally(...). + /// + /// The result type. + /// The asynchronous factory, invoked with a linked cancellation token. + /// The outer cancellation source, cancelled by the caller and disposed when the run ends. + private sealed class FromAsyncObservable(Func> factory, CancellationTokenSource outerCancellation) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var run = new Run(observer, factory, outerCancellation); + run.Start(); + return run; + } + + /// Drives a single asynchronous execution, forwarding its result or error exactly once. + private sealed class Run : IDisposable + { + /// The observer receiving the result. + private readonly IObserver _observer; + + /// The asynchronous factory. + private readonly Func> _factory; + + /// The outer cancellation source (caller-cancellable), disposed when the run ends. + private readonly CancellationTokenSource _outerCancellation; + + /// The subscription cancellation source, cancelled on dispose. + private readonly CancellationTokenSource _subscriptionCancellation = new(); + + /// Zero until the result or error has been forwarded. + private int _emitted; - return Observable.Return<(IObservable Result, Action Cancel)>((result, () => cts.Cancel())); - }); + /// Zero until the cancellation sources have been cleaned up. + private int _disposed; + + /// Initializes a new instance of the class. + /// The observer receiving the result. + /// The asynchronous factory. + /// The outer cancellation source. + public Run(IObserver observer, Func> factory, CancellationTokenSource outerCancellation) + { + _observer = observer; + _factory = factory; + _outerCancellation = outerCancellation; + } + + /// Starts the asynchronous execution. + public void Start() => _ = RunAsync(); + + /// + public void Dispose() + { + if (Interlocked.Exchange(ref _disposed, 1) != 0) + { + return; + } + + _subscriptionCancellation.Cancel(); + _subscriptionCancellation.Dispose(); + _outerCancellation.Dispose(); + } + + /// Awaits the factory and forwards the result or error, cleaning up afterwards. + /// A task that completes when the result has been forwarded. + private async Task RunAsync() + { + try + { + using var linked = CancellationTokenSource.CreateLinkedTokenSource(_outerCancellation.Token, _subscriptionCancellation.Token); + var value = await _factory(linked.Token).ConfigureAwait(false); + if (Interlocked.Exchange(ref _emitted, 1) == 0) + { + _observer.OnNext(value); + _observer.OnCompleted(); + } + } + catch (Exception ex) + { + if (Interlocked.Exchange(ref _emitted, 1) == 0) + { + _observer.OnError(ex); + } + } + finally + { + Dispose(); + } + } + } + } } diff --git a/src/ReactiveUI/Mixins/ObservedChangedMixin.cs b/src/ReactiveUI/Mixins/ObservedChangedMixin.cs index 548fc6d1d8..848d8fdd27 100644 --- a/src/ReactiveUI/Mixins/ObservedChangedMixin.cs +++ b/src/ReactiveUI/Mixins/ObservedChangedMixin.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + using ReactiveUI.Builder; namespace ReactiveUI; @@ -49,12 +51,14 @@ item is null /// Thrown if the item parameter is null. /// Thrown if any property in the observed property chain is null, preventing the value from being retrieved. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static TValue GetValue(this IObservedChange item) => - item is null - ? throw new ArgumentNullException(nameof(item)) - : !item.TryGetValue(out var returnValue) - ? throw new Exception($"One of the properties in the expression '{item.GetPropertyName()}' was null") - : returnValue; + public static TValue GetValue(this IObservedChange item) + { + ArgumentExceptionHelper.ThrowIfNull(item); + return item.TryGetValue(out var returnValue) + ? returnValue + : throw new InvalidOperationException( + $"One of the properties in the expression '{item.GetPropertyName()}' was null"); + } /// /// Gets the current value from the observed change, or the default value for the type if the value cannot be @@ -67,8 +71,13 @@ item is null /// . /// Thrown if is null. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static TValue? GetValueOrDefault(this IObservedChange item) => // TODO: Create Test - item is null ? throw new ArgumentNullException(nameof(item)) : !item.TryGetValue(out var returnValue) ? default : returnValue; + public static TValue? + GetValueOrDefault(this IObservedChange item) + { + ArgumentExceptionHelper.ThrowIfNull(item); + + return !item.TryGetValue(out var returnValue) ? default : returnValue; + } /// /// Projects each observed change notification to the current value of the observed property or member chain. @@ -82,8 +91,9 @@ item is null /// An observable sequence that emits the current value of the observed property or member chain each time a change /// notification is received. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static IObservable Value(this IObservable> item) => // TODO: Create Test - item.Select(GetValue); + public static IObservable + Value(this IObservable> item) => + new ValueObservable(item); /// /// Attempts to retrieve the value associated with the observed change, using the value directly if available or @@ -99,7 +109,9 @@ public static IObservable Value(this IObservable /// true if the value was successfully retrieved; otherwise, false. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - internal static bool TryGetValue(this IObservedChange item, out TValue changeValue) + internal static bool TryGetValue( + this IObservedChange item, + out TValue changeValue) { if (!Equals(item.Value, default(TValue))) { @@ -107,7 +119,10 @@ internal static bool TryGetValue(this IObservedChange @@ -129,9 +144,61 @@ internal static void SetValueToProperty( TTarget target, Expression> property) { - if (target is not null) + if (target is null) + { + return; + } + + Reflection.TrySetValueToPropertyChain( + target, + Reflection.Rewrite(property.Body).GetExpressionChain(), + item.GetValue()); + } + + /// + /// A fused sink that projects each observed change to its current value via — + /// replacing the Select(GetValue) used by . + /// + /// The type of the object that owns the observed member. + /// The observed value type. + /// The source stream of observed changes. + [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + private sealed class ValueObservable(IObservable> source) + : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) { - Reflection.TrySetValueToPropertyChain(target, Reflection.Rewrite(property.Body).GetExpressionChain(), item.GetValue()); + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// Reads the current value from each observed change and forwards it. + [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + private sealed class Sink(IObserver downstream) : IObserver> + { + /// + public void OnNext(IObservedChange value) + { + TValue projected; + try + { + projected = value.GetValue(); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(projected); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); } } } diff --git a/src/ReactiveUI/Mixins/PreserveAttribute.cs b/src/ReactiveUI/Mixins/PreserveAttribute.cs index 20747362f2..b87f1c5ab7 100644 --- a/src/ReactiveUI/Mixins/PreserveAttribute.cs +++ b/src/ReactiveUI/Mixins/PreserveAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs b/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs index 1800430546..bcd52d6d78 100644 --- a/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs +++ b/src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs @@ -1,9 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reflection; using ReactiveUI.Builder; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -25,8 +28,11 @@ namespace ReactiveUI; "Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] public static class ReactiveNotifyPropertyChangedMixin { + /// + /// Caches the best available property-notification factory for each sender type, property name, and change timing. + /// private static readonly - MemoizingMRUCache<(Type senderType, string propertyName, bool beforeChange), ICreatesObservableForProperty?> + MemoizingMRUCache<(Type? senderType, string propertyName, bool beforeChange), ICreatesObservableForProperty?> _notifyFactoryCache = new( (t, _) => AppLocator.Current.GetServices() @@ -61,6 +67,10 @@ private static readonly /// An Observable representing the property change notifications for the given property name. [RequiresUnreferencedCode( "Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> ObservableForProperty( this TSender? item, string propertyName, @@ -84,28 +94,28 @@ public static IObservable> ObservableForPropert expr = parameter; } - var factory = _notifyFactoryCache.Get((item!.GetType(), propertyName, beforeChange)) - ?? throw new Exception( - $"Could not find a ICreatesObservableForProperty for {item!.GetType()} property {propertyName}. This should never happen, your service locator is probably broken. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance."); + var factory = _notifyFactoryCache.Get((item.GetType(), propertyName, beforeChange)) + ?? throw new InvalidOperationException( + $"Could not find a ICreatesObservableForProperty for {item.GetType()} property {propertyName}. " + + "This should never happen, your service locator is probably broken. Please make sure you have installed " + + "the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance."); // Helper to get current property value without expression analysis. - static TValue GetCurrentValue(object sender, string name) + static TValue GetCurrentValue(TSender sender, string name) { - var t = sender.GetType(); + var t = sender?.GetType(); #if NETSTANDARD || NETFRAMEWORK var prop = - t.GetProperty(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy); + t?.GetProperty( + name, + System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy); #else - var prop = t.GetProperty( + var prop = t?.GetProperty( name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy); #endif - if (prop is null) - { - return default!; - } - var val = prop.GetValue(sender); + var val = prop?.GetValue(sender); if (val is null) { return default!; @@ -114,49 +124,8 @@ static TValue GetCurrentValue(object sender, string name) return val is TValue tv ? tv : (TValue)val; } - var core = Observable.Create>(obs => - { - // Emit initial value if requested. - if (!skipInitial) - { - try - { - var initial = GetCurrentValue(item!, propertyName); - obs.OnNext(new ObservedChange(item!, expr, initial)); - } - catch (Exception ex) - { - obs.OnError(ex); - } - } - - var subscription = factory - .GetNotificationForProperty(item!, expr, propertyName, beforeChange, suppressWarnings: false) - .Subscribe( - _ => - { - try - { - var current = GetCurrentValue(item!, propertyName); - obs.OnNext(new ObservedChange(item!, expr, current)); - } - catch (Exception ex) - { - obs.OnError(ex); - } - }, - obs.OnError, - obs.OnCompleted); - - return subscription; - }); - - if (isDistinct) - { - return core.DistinctUntilChanged(x => x.Value); - } - - return core; + var notifications = factory.GetNotificationForProperty(item, expr, propertyName, beforeChange, suppressWarnings: false); + return new ObservableForPropertySink(item, expr, propertyName, notifications, GetCurrentValue, skipInitial, isDistinct); } /// @@ -169,6 +138,10 @@ static TValue GetCurrentValue(object sender, string name) /// An observable sequence of observed changes for the given property name. [RequiresUnreferencedCode( "Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> ObservableForProperty( this TSender? item, string propertyName) @@ -190,6 +163,10 @@ public static IObservable> ObservableForPropert /// An observable sequence of observed changes for the given property name. [RequiresUnreferencedCode( "Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> ObservableForProperty( this TSender? item, string propertyName, @@ -213,6 +190,10 @@ public static IObservable> ObservableForPropert /// An observable sequence of observed changes for the given property name. [RequiresUnreferencedCode( "Creating Expressions requires unreferenced code because the members being referenced by the Expression may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> ObservableForProperty( this TSender? item, string propertyName, @@ -319,6 +300,7 @@ public static IObservable> ObservableForPropert /// notifications for the given property. /// [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + [SuppressMessage("Major Code Smell", "S125:Sections of code should not be commented out", Justification = "False positive, pseudo code.")] public static IObservable> ObservableForProperty( this TSender? item, Expression> property, @@ -371,13 +353,13 @@ public static IObservable> ObservableForPropert public static IObservable ObservableForProperty( this TSender? item, Expression> property, - Func selector) // TODO: Create Test + Func selector) where TSender : class { ArgumentExceptionHelper.ThrowIfNull(property); ArgumentExceptionHelper.ThrowIfNull(selector); - return item.ObservableForProperty(property, false).Select(x => selector(x.Value)); + return new ObservedChangeValueSelector(item.ObservableForProperty(property, false), selector); } /// @@ -403,13 +385,13 @@ public static IObservable ObservableForProperty( this TSender? item, Expression> property, Func selector, - bool beforeChange) // TODO: Create Test + bool beforeChange) where TSender : class { ArgumentExceptionHelper.ThrowIfNull(property); ArgumentExceptionHelper.ThrowIfNull(selector); - return item.ObservableForProperty(property, beforeChange).Select(x => selector(x.Value)); + return new ObservedChangeValueSelector(item.ObservableForProperty(property, beforeChange), selector); } /// @@ -426,9 +408,13 @@ public static IObservable ObservableForProperty( /// /// If we cannot cast from the target value from the specified last property. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> SubscribeToExpressionChain( this TSender? source, - Expression? expression) // TODO: Create Test + Expression? expression) => SubscribeToExpressionChain(source, expression, false, true, false, true); /// @@ -446,10 +432,14 @@ public static IObservable> SubscribeToExpressio /// /// If we cannot cast from the target value from the specified last property. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> SubscribeToExpressionChain( this TSender? source, Expression? expression, - bool beforeChange) // TODO: Create Test + bool beforeChange) => SubscribeToExpressionChain(source, expression, beforeChange, true, false, true); /// @@ -468,11 +458,15 @@ public static IObservable> SubscribeToExpressio /// /// If we cannot cast from the target value from the specified last property. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> SubscribeToExpressionChain( this TSender? source, Expression? expression, bool beforeChange, - bool skipInitial) // TODO: Create Test + bool skipInitial) => SubscribeToExpressionChain(source, expression, beforeChange, skipInitial, false, true); /// @@ -492,12 +486,16 @@ public static IObservable> SubscribeToExpressio /// /// If we cannot cast from the target value from the specified last property. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> SubscribeToExpressionChain( this TSender? source, Expression? expression, bool beforeChange, bool skipInitial, - bool suppressWarnings) // TODO: Create Test + bool suppressWarnings) => SubscribeToExpressionChain( source, expression, @@ -524,81 +522,29 @@ public static IObservable> SubscribeToExpressio /// /// If we cannot cast from the target value from the specified last property. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable> SubscribeToExpressionChain( this TSender? source, Expression? expression, bool beforeChange, bool skipInitial, bool suppressWarnings, - bool isDistinct) // TODO: Create Test - { - IObservable> notifier = - Observable.Return(new ObservedChange(null, null, source)); - - var chain = Reflection.Rewrite(expression).GetExpressionChain(); - notifier = chain.Aggregate( - notifier, - (n, expr) => n - .Select(y => NestedObservedChanges(expr, y, beforeChange, suppressWarnings)) - .Switch()); - - if (skipInitial) - { - notifier = notifier.Skip(1); - } - - notifier = notifier.Where(x => x.Sender is not null); - - var r = notifier.Select(x => - { - // ensure cast to TValue will succeed, throw useful exception otherwise - var val = x.GetValue(); - if (val is not null && val is not TValue) - { - throw new InvalidCastException($"Unable to cast from {val.GetType()} to {typeof(TValue)}."); - } - - return new ObservedChange(source!, expression, (TValue)val!); - }); - - return isDistinct ? r.DistinctUntilChanged(x => x.Value) : r; - } - - /// - /// Creates an observable sequence that emits observed changes for each member in an expression-based property - /// chain, starting from a given source change. - /// - /// This method uses reflection to evaluate the expression-based member chain. If the source - /// value is null, the returned sequence contains only the initial change. Otherwise, it tracks changes for each - /// property in the chain. Reflection-based member access may be affected by trimming in some deployment - /// scenarios. - /// An expression representing the property or member chain to observe for changes. - /// The initial observed change that serves as the starting point for tracking nested property changes. - /// true to observe property values before they change; otherwise, false to observe values after the change. - /// true to suppress warnings related to property observation; otherwise, false. - /// An observable sequence of observed changes for each member in the specified property chain. The sequence emits - /// an initial change corresponding to the source, followed by subsequent changes as properties in the chain are - /// updated. - [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - private static IObservable> NestedObservedChanges( - Expression expression, - IObservedChange sourceChange, - bool beforeChange, - bool suppressWarnings) + bool isDistinct) { - // Make sure a change at a root node propagates events down - var kicker = new ObservedChange(sourceChange.Value, expression, default); - - // Handle null values in the chain - if (sourceChange.Value is null) - { - return Observable.Return(kicker); - } - - // Handle non null values in the chain - return NotifyForProperty(sourceChange.Value, expression, beforeChange, suppressWarnings) - .StartWith(kicker) - .Select(static x => new ObservedChange(x.Sender, x.Expression, x.GetValueOrDefault())); + Expression[] links = [.. Reflection.Rewrite(expression).GetExpressionChain()]; + return new ExpressionChainSink( + new ExpressionChainParameters( + source, + expression, + links, + beforeChange, + suppressWarnings, + skipInitial, + isDistinct, + NotifyForProperty)); } /// @@ -634,7 +580,9 @@ public static IObservable> SubscribeToExpressio return result switch { null => throw new InvalidOperationException( - $"Could not find a ICreatesObservableForProperty for {sender.GetType()} property {propertyName}. This should never happen, your service locator is probably broken. Please make sure you have installed the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance."), + $"Could not find a ICreatesObservableForProperty for {sender.GetType()} property {propertyName}." + + " This should never happen, your service locator is probably broken. Please make sure you have installed " + + "the latest version of the ReactiveUI packages for your platform. See https://reactiveui.net/docs/getting-started/installation for guidance."), _ => result.GetNotificationForProperty(sender, expression, propertyName, beforeChange, suppressWarnings) }; } diff --git a/src/ReactiveUI/Mixins/SwitchSubscribeMixin.cs b/src/ReactiveUI/Mixins/SwitchSubscribeMixin.cs index 35121873da..c6ee82b457 100644 --- a/src/ReactiveUI/Mixins/SwitchSubscribeMixin.cs +++ b/src/ReactiveUI/Mixins/SwitchSubscribeMixin.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -46,10 +48,8 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(source); ArgumentExceptionHelper.ThrowIfNull(onNext); - return source - .WhereNotNull() - .Switch() - .Subscribe(onNext); + return new SwitchSelectObservable, T>(source, static x => x) + .Subscribe(new DelegateObserver(onNext)); } /// @@ -74,48 +74,8 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(onError); ArgumentExceptionHelper.ThrowIfNull(onCompleted); - return source - .WhereNotNull() - .Switch() - .Subscribe(onNext, onError, onCompleted); - } - - /// - /// Projects each inner observable emitted by the source using the specified selector, - /// then switches to the projected observable. - /// - /// The type of the source inner observables. - /// The type of values in the projected observables. - /// An observable that emits other observables. - /// A transform function to apply to each inner observable. - /// An observable sequence whose elements are the result of invoking the transform function on each inner observable and switching to it. - /// Thrown when or is null. - /// - /// - /// // Subscribe to IsExecuting from a command property that can change - /// this.WhenAnyValue(x => x.Command) - /// .SwitchSubscribe( - /// cmd => cmd.IsExecuting, - /// isExecuting => IsBusy = isExecuting - /// ); - /// - /// // Or use with ToProperty - /// _isBusy = this.WhenAnyValue(x => x.Command) - /// .SwitchSelect(cmd => cmd.IsExecuting) - /// .ToProperty(this, x => x.IsBusy); - /// - /// - public static IObservable SwitchSelect( - this IObservable source, - Func> selector) - { - ArgumentExceptionHelper.ThrowIfNull(source); - ArgumentExceptionHelper.ThrowIfNull(selector); - - return source - .WhereNotNull() - .Select(selector) - .Switch(); + return new SwitchSelectObservable, T>(source, static x => x) + .Subscribe(new DelegateObserver(onNext, onError, onCompleted)); } /// @@ -148,9 +108,8 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(selector); ArgumentExceptionHelper.ThrowIfNull(onNext); - return source - .SwitchSelect(selector) - .Subscribe(onNext); + return new SwitchSelectObservable(source, selector) + .Subscribe(new DelegateObserver(onNext)); } /// @@ -179,9 +138,8 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(onError); ArgumentExceptionHelper.ThrowIfNull(onCompleted); - return source - .SwitchSelect(selector) - .Subscribe(onNext, onError, onCompleted); + return new SwitchSelectObservable(source, selector) + .Subscribe(new DelegateObserver(onNext, onError, onCompleted)); } /// @@ -208,10 +166,8 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(source); ArgumentExceptionHelper.ThrowIfNull(onNext); - return source - .WhereNotNull() - .Switch() - .Subscribe(onNext); + return new SwitchSelectObservable, TResult>(source, static cmd => cmd) + .Subscribe(new DelegateObserver(onNext)); } /// @@ -237,49 +193,8 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(onError); ArgumentExceptionHelper.ThrowIfNull(onCompleted); - return source - .WhereNotNull() - .Switch() - .Subscribe(onNext, onError, onCompleted); - } - - /// - /// Projects a command property to one of its observables (e.g., IsExecuting, CanExecute), - /// automatically switching when the command property changes. - /// - /// The command parameter type. - /// The command result type. - /// The type of values emitted by the selected observable. - /// An observable that emits ReactiveCommand instances. - /// A function to select an observable from the command (e.g., cmd => cmd.IsExecuting). - /// An observable sequence that switches to the selected observable whenever the command changes. - /// Thrown when or is null. - /// - /// - /// // Use with ToProperty to track IsExecuting from a replaceable command - /// _isBusy = this.WhenAnyValue(x => x.SaveCommand) - /// .SwitchSelect(cmd => cmd.IsExecuting) - /// .ToProperty(this, x => x.IsBusy); - /// - /// // Or subscribe directly - /// this.WhenAnyValue(x => x.DeleteCommand) - /// .SwitchSubscribe( - /// cmd => cmd.CanExecute, - /// canExecute => DeleteButtonEnabled = canExecute - /// ); - /// - /// - public static IObservable SwitchSelect( - this IObservable?> source, - Func, IObservable> selector) - { - ArgumentExceptionHelper.ThrowIfNull(source); - ArgumentExceptionHelper.ThrowIfNull(selector); - - return source - .WhereNotNull() - .Select(selector) - .Switch(); + return new SwitchSelectObservable, TResult>(source, static cmd => cmd) + .Subscribe(new DelegateObserver(onNext, onError, onCompleted)); } /// @@ -313,9 +228,8 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(selector); ArgumentExceptionHelper.ThrowIfNull(onNext); - return source - .SwitchSelect(selector) - .Subscribe(onNext); + return new SwitchSelectObservable, TValue>(source, selector) + .Subscribe(new DelegateObserver(onNext)); } /// @@ -345,8 +259,297 @@ public static IDisposable SwitchSubscribe( ArgumentExceptionHelper.ThrowIfNull(onError); ArgumentExceptionHelper.ThrowIfNull(onCompleted); - return source - .SwitchSelect(selector) - .Subscribe(onNext, onError, onCompleted); + return new SwitchSelectObservable, TValue>(source, selector) + .Subscribe(new DelegateObserver(onNext, onError, onCompleted)); + } + + /// + /// Projects each inner observable emitted by the source using the specified selector, + /// then switches to the projected observable. + /// + /// The type of the source inner observables. + /// The type of values in the projected observables. + /// An observable that emits other observables. + /// A transform function to apply to each inner observable. + /// An observable sequence whose elements are the result of invoking the transform function on each inner observable and switching to it. + /// Thrown when or is null. + /// + /// + /// // Subscribe to IsExecuting from a command property that can change + /// this.WhenAnyValue(x => x.Command) + /// .SwitchSubscribe( + /// cmd => cmd.IsExecuting, + /// isExecuting => IsBusy = isExecuting + /// ); + /// + /// // Or use with ToProperty + /// _isBusy = this.WhenAnyValue(x => x.Command) + /// .SwitchSelect(cmd => cmd.IsExecuting) + /// .ToProperty(this, x => x.IsBusy); + /// + /// + public static IObservable SwitchSelect( + this IObservable source, + Func> selector) + { + ArgumentExceptionHelper.ThrowIfNull(source); + ArgumentExceptionHelper.ThrowIfNull(selector); + + return new SwitchSelectObservable(source, selector); + } + + /// + /// Projects a command property to one of its observables (e.g., IsExecuting, CanExecute), + /// automatically switching when the command property changes. + /// + /// The command parameter type. + /// The command result type. + /// The type of values emitted by the selected observable. + /// An observable that emits ReactiveCommand instances. + /// A function to select an observable from the command (e.g., cmd => cmd.IsExecuting). + /// An observable sequence that switches to the selected observable whenever the command changes. + /// Thrown when or is null. + /// + /// + /// // Use with ToProperty to track IsExecuting from a replaceable command + /// _isBusy = this.WhenAnyValue(x => x.SaveCommand) + /// .SwitchSelect(cmd => cmd.IsExecuting) + /// .ToProperty(this, x => x.IsBusy); + /// + /// // Or subscribe directly + /// this.WhenAnyValue(x => x.DeleteCommand) + /// .SwitchSubscribe( + /// cmd => cmd.CanExecute, + /// canExecute => DeleteButtonEnabled = canExecute + /// ); + /// + /// + public static IObservable SwitchSelect( + this IObservable?> source, + Func, IObservable> selector) + { + ArgumentExceptionHelper.ThrowIfNull(source); + ArgumentExceptionHelper.ThrowIfNull(selector); + + return new SwitchSelectObservable, TValue>(source, selector); + } + + /// + /// A fused sink that filters out null source values, projects each remaining value to an inner observable via + /// , and switches to the latest inner observable — replacing the + /// WhereNotNull().Select(selector).Switch() chain with a single allocation-tuned operator. + /// + /// The (nullable) source element type. + /// The element type of the projected inner observables. + /// The source observable. + /// Projects each non-null source value to an inner observable. + private sealed class SwitchSelectObservable( + IObservable source, + Func> selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + var sink = new Sink(selector, observer); + sink.Run(source); + return sink; + } + + /// Subscribes to the source, switching the active inner subscription on each non-null value. + private sealed class Sink(Func> selector, IObserver downstream) + : IObserver, IDisposable + { + /// Guards the switching state so outer and inner notifications stay consistent. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The outer (source) subscription. + private readonly OnceDisposable _outer = new(); + + /// The active inner subscription; assigning a new value disposes the previous one. + private readonly SwapDisposable _inner = new(); + + /// Generation id of the most recent inner observable; stale inner notifications are ignored. + private ulong _latest; + + /// Whether an inner subscription is currently active. + private bool _hasInner; + + /// Whether the outer source has completed. + private bool _outerCompleted; + + /// Whether this sink has been disposed. + private bool _disposed; + + /// Begins observing the source. + /// The source observable. + public void Run(IObservable source) => _outer.Disposable = source.Subscribe(this); + + /// + public void OnNext(TSource? value) + { + if (value is null) + { + return; + } + + IObservable inner; + try + { + inner = selector(value); + } + catch (Exception ex) + { + OnError(ex); + return; + } + + ulong id; + lock (_gate) + { + if (_disposed) + { + return; + } + + id = ++_latest; + _hasInner = true; + } + + _inner.Disposable = inner.Subscribe(new InnerObserver(this, id)); + } + + /// + public void OnError(Exception error) + { + lock (_gate) + { + if (_disposed) + { + return; + } + } + + downstream.OnError(error); + Dispose(); + } + + /// + public void OnCompleted() + { + bool complete; + lock (_gate) + { + if (_disposed) + { + return; + } + + _outerCompleted = true; + complete = !_hasInner; + } + + if (!complete) + { + return; + } + + downstream.OnCompleted(); + Dispose(); + } + + /// + public void Dispose() + { + lock (_gate) + { + if (_disposed) + { + return; + } + + _disposed = true; + } + + _outer.Dispose(); + _inner.Dispose(); + } + + /// Forwards an inner value to the downstream observer if it belongs to the active inner subscription. + /// The generation id of the inner subscription that produced the value. + /// The value to forward. + private void InnerOnNext(ulong id, TResult value) + { + lock (_gate) + { + if (_disposed || id != _latest) + { + return; + } + } + + downstream.OnNext(value); + } + + /// Forwards an inner error to the downstream observer if it belongs to the active inner subscription. + /// The generation id of the inner subscription that errored. + /// The error to forward. + private void InnerOnError(ulong id, Exception error) + { + lock (_gate) + { + if (_disposed || id != _latest) + { + return; + } + } + + downstream.OnError(error); + Dispose(); + } + + /// Clears the active inner subscription; completes downstream only if the outer has also completed. + /// The generation id of the inner subscription that completed. + private void InnerOnCompleted(ulong id) + { + bool complete; + lock (_gate) + { + if (_disposed || id != _latest) + { + return; + } + + _hasInner = false; + complete = _outerCompleted; + } + + if (!complete) + { + return; + } + + downstream.OnCompleted(); + Dispose(); + } + + /// Forwards a single inner subscription's notifications, tagged with its generation id. + private sealed class InnerObserver(Sink parent, ulong id) : IObserver + { + /// + public void OnNext(TResult value) => parent.InnerOnNext(id, value); + + /// + public void OnError(Exception error) => parent.InnerOnError(id, error); + + /// + public void OnCompleted() => parent.InnerOnCompleted(id); + } + } } } diff --git a/src/ReactiveUI/Observable.cs b/src/ReactiveUI/Observable.cs index 104205e7fd..ae92fb602c 100644 --- a/src/ReactiveUI/Observable.cs +++ b/src/ReactiveUI/Observable.cs @@ -1,12 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using ReactiveUI.Internal; + namespace System.Reactive.Linq; /// -/// Provides commonly required, statically-allocated, pre-canned observables. +/// Provides commonly required, statically-allocated, pre-canned observables, backed by tailored sinks. /// /// /// The observable type. @@ -16,15 +18,15 @@ internal static class Observable /// /// An empty observable of type . /// - public static readonly IObservable Empty = Observable.Empty(); + public static readonly IObservable Empty = EmptyObservable.Instance; /// /// An observable of type that never ticks a value. /// - public static readonly IObservable Never = Observable.Never(); + public static readonly IObservable Never = NeverObservable.Instance; /// /// An observable of type that ticks a single, default value. /// - public static readonly IObservable Default = Observable.Return(default(T)!); + public static readonly IObservable Default = new SingleValueObservable(default!); } diff --git a/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs index c6befeb150..91df1e1430 100644 --- a/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/INPCObservableForProperty.cs @@ -1,8 +1,11 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; +using System.Linq.Expressions; +using System.Reactive.Linq; using System.Reflection; namespace ReactiveUI; @@ -16,69 +19,234 @@ namespace ReactiveUI; /// typically used in reactive programming scenarios to monitor property changes in data-binding or MVVM patterns. /// Reflection is used to inspect runtime types, which may have implications for trimming or ahead-of-time (AOT) /// compilation. +[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Legacy naming convention")] +[SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Legacy naming convention")] public class INPCObservableForProperty : ICreatesObservableForProperty { /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged) + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged) { + if (type is null) + { + return 0; + } + var target = beforeChanged ? typeof(INotifyPropertyChanging) : typeof(INotifyPropertyChanged); - return target.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) ? 5 : 0; + return target.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) ? BindingAffinity.Explicit : 0; } /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false) + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged, + bool suppressWarnings) { ArgumentExceptionHelper.ThrowIfNull(expression); + var expectedName = expression.NodeType == ExpressionType.Index ? propertyName + "[]" : propertyName; + if (beforeChanged && sender is INotifyPropertyChanging before) { - var obs = Observable.FromEvent( - eventHandler => - { - void Handler(object? eventSender, PropertyChangingEventArgs e) => eventHandler(e.PropertyName); - return Handler; - }, - x => before!.PropertyChanging += x, - x => before!.PropertyChanging -= x); - - if (expression.NodeType == ExpressionType.Index) - { - return obs.Where(x => string.IsNullOrEmpty(x) - || x?.Equals(propertyName + "[]", StringComparison.InvariantCulture) == true) - .Select(_ => new ObservedChange(sender, expression, default)); - } + return new BeforeChangeNotification(before, sender, expression, expectedName); + } - return obs.Where(x => string.IsNullOrEmpty(x) - || x?.Equals(propertyName, StringComparison.InvariantCulture) == true) - .Select(_ => new ObservedChange(sender, expression, default)); + if (sender is INotifyPropertyChanged after) + { + return new ChangeNotification(after, sender, expression, expectedName); } - else if (sender is INotifyPropertyChanged after) + + return Observable>.Never; + } + + /// + /// Determines whether a notified property name matches the observed property (an empty name means "all properties"). + /// + /// The property name carried by the notification. + /// The observed property name. + /// if the notification applies to the observed property. + private static bool Matches(string? notifiedName, string expectedName) => + string.IsNullOrEmpty(notifiedName) || + string.Equals(notifiedName, expectedName, StringComparison.InvariantCulture); + + /// + /// A single-layer observable over : each subscription attaches + /// a handler that filters by name and emits the observed change directly, with no intermediate operators. + /// + /// The change notifier to hook. + /// The object surfaced on the observed change. + /// The expression surfaced on the observed change. + /// The observed property name. + private sealed class ChangeNotification( + INotifyPropertyChanged notifier, + object sender, + Expression expression, + string expectedName) : IObservable> + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Subscription(notifier, sender, expression, expectedName, observer); + } + + /// Attaches the property-changed handler for the lifetime of the subscription. + private sealed class Subscription : IDisposable { - var obs = Observable.FromEvent( - eventHandler => - { - void Handler(object? eventSender, PropertyChangedEventArgs e) => eventHandler(e.PropertyName); - return Handler; - }, - x => after!.PropertyChanged += x, - x => after!.PropertyChanged -= x); - - if (expression.NodeType == ExpressionType.Index) + /// The change notifier this subscription is hooked to. + private readonly INotifyPropertyChanged _notifier; + + /// The object surfaced on the observed change. + private readonly object _sender; + + /// The expression surfaced on the observed change. + private readonly Expression _expression; + + /// The observed property name. + private readonly string _expectedName; + + /// The observer receiving observed changes. + private readonly IObserver> _observer; + + /// Initializes a new instance of the class and hooks the event. + /// The change notifier to hook. + /// The object surfaced on the observed change. + /// The expression surfaced on the observed change. + /// The observed property name. + /// The observer receiving observed changes. + public Subscription( + INotifyPropertyChanged notifier, + object sender, + Expression expression, + string expectedName, + IObserver> observer) { - return obs.Where(x => string.IsNullOrEmpty(x) - || x?.Equals(propertyName + "[]", StringComparison.InvariantCulture) == true) - .Select(_ => new ObservedChange(sender, expression, default)); + _notifier = notifier; + _sender = sender; + _expression = expression; + _expectedName = expectedName; + _observer = observer; + _notifier.PropertyChanged += OnPropertyChanged; } - return obs.Where(x => string.IsNullOrEmpty(x) - || x?.Equals(propertyName, StringComparison.InvariantCulture) == true) - .Select(_ => new ObservedChange(sender, expression, default)); + /// + public void Dispose() => _notifier.PropertyChanged -= OnPropertyChanged; + + /// Filters the changed property name and forwards a matching observed change. + /// The event sender. + /// The property-changed event arguments. + private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (!Matches(e.PropertyName, _expectedName)) + { + return; + } + + _observer.OnNext(new ObservedChange(_sender, _expression, null)); + } + } + } + + /// + /// A single-layer observable over : each subscription attaches + /// a handler that filters by name and emits the observed change directly, with no intermediate operators. + /// + /// The change notifier to hook. + /// The object surfaced on the observed change. + /// The expression surfaced on the observed change. + /// The observed property name. + private sealed class BeforeChangeNotification( + INotifyPropertyChanging notifier, + object sender, + Expression expression, + string expectedName) : IObservable> + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Subscription(notifier, sender, expression, expectedName, observer); } - else + + /// Attaches the property-changing handler for the lifetime of the subscription. + private sealed class Subscription : IDisposable { - return Observable>.Never; + /// The change notifier this subscription is hooked to. + private readonly INotifyPropertyChanging _notifier; + + /// The object surfaced on the observed change. + private readonly object _sender; + + /// The expression surfaced on the observed change. + private readonly Expression _expression; + + /// The observed property name. + private readonly string _expectedName; + + /// The observer receiving observed changes. + private readonly IObserver> _observer; + + /// Initializes a new instance of the class and hooks the event. + /// The change notifier to hook. + /// The object surfaced on the observed change. + /// The expression surfaced on the observed change. + /// The observed property name. + /// The observer receiving observed changes. + public Subscription( + INotifyPropertyChanging notifier, + object sender, + Expression expression, + string expectedName, + IObserver> observer) + { + _notifier = notifier; + _sender = sender; + _expression = expression; + _expectedName = expectedName; + _observer = observer; + _notifier.PropertyChanging += OnPropertyChanging; + } + + /// + public void Dispose() => _notifier.PropertyChanging -= OnPropertyChanging; + + /// Filters the changing property name and forwards a matching observed change. + /// The event sender. + /// The property-changing event arguments. + private void OnPropertyChanging(object? sender, PropertyChangingEventArgs e) + { + if (!Matches(e.PropertyName, _expectedName)) + { + return; + } + + _observer.OnNext(new ObservedChange(_sender, _expression, null)); + } } } } diff --git a/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs index 1253fca043..7a88c2e198 100644 --- a/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/IROObservableForProperty.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; using System.Reflection; namespace ReactiveUI; @@ -20,42 +21,62 @@ namespace ReactiveUI; /// repeats the required annotations on its public members to satisfy the interface contract. /// /// +[SuppressMessage( + "Minor Code Smell", + "S101:Types should be named in PascalCase", + Justification = "Established public API; renaming is breaking.")] public sealed class IROObservableForProperty : ICreatesObservableForProperty { + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + /// /// /// This implementation returns a higher affinity than the INPC-based implementation because every - /// also implements property change notification and should be preferred when available. + /// IReactiveObject also implements property change notification and should be preferred when available. /// - /// The runtime type to query. - /// The property name to query. - /// - /// If , indicates the caller requests notifications before the property value changes. - /// If , indicates after-change notifications. - /// - /// - /// A positive integer if supported; zero otherwise. - /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged) { - ArgumentExceptionHelper.ThrowIfNull(type); ArgumentExceptionHelper.ThrowIfNull(propertyName); - // NB: Since every IReactiveObject is also an INPC, we need to bind more tightly than INPCObservableForProperty. - return typeof(IReactiveObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) ? 10 : 0; + if (type is null) + { + return 0; + } + + return typeof(IReactiveObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) ? BindingAffinity.ExactType : 0; } /// /// Thrown when is . /// Thrown when does not implement . [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] public IObservable> GetNotificationForProperty( object sender, Expression expression, string propertyName, - bool beforeChanged = false, - bool suppressWarnings = false) + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged, + bool suppressWarnings) { ArgumentExceptionHelper.ThrowIfNull(sender); ArgumentExceptionHelper.ThrowIfNull(expression); @@ -66,21 +87,64 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang throw new ArgumentException("Sender doesn't implement IReactiveObject", nameof(sender)); } - // For indexers, ReactiveObject reports "PropertyName[]". var observedName = expression.NodeType == ExpressionType.Index - ? string.Concat(propertyName, "[]") + ? $"{propertyName}[]" : propertyName; - // Preserve the original comparison semantics. - const StringComparison comparison = StringComparison.InvariantCulture; - var source = beforeChanged ? iro.GetChangingObservable() : iro.GetChangedObservable(); - // Keep the projection allocation-free; avoid repeating the same query shape. - return source - .Where(x => x.PropertyName is not null && x.PropertyName.Equals(observedName, comparison)) - .Select(static _ => default(object)) - .Select(_ => new ObservedChange(sender, expression, default)); + return new FilteredChange(source, sender, expression, observedName); + } + + /// + /// A single-layer observable that filters a reactive object's change stream to the observed property name and + /// projects each matching notification into an observed change, with no intermediate operators. + /// + /// The reactive object's change notifications. + /// The object surfaced on the observed change. + /// The expression surfaced on the observed change. + /// The observed property name. + private sealed class FilteredChange( + IObservable> source, + object sender, + Expression expression, + string observedName) : IObservable> + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Observer(observer, sender, expression, observedName)); + } + + /// Filters change-args by name and forwards a projected observed change. + /// The observer receiving observed changes. + /// The object surfaced on the observed change. + /// The expression surfaced on the observed change. + /// The observed property name. + private sealed class Observer( + IObserver> downstream, + object sender, + Expression expression, + string observedName) : IObserver> + { + /// + public void OnNext(IReactivePropertyChangedEventArgs value) + { + if (!string.Equals(value.PropertyName, observedName, StringComparison.InvariantCulture)) + { + return; + } + + downstream.OnNext(new ObservedChange(sender, expression, null)); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } } } diff --git a/src/ReactiveUI/ObservableForProperty/OAPHCreationHelperMixin.cs b/src/ReactiveUI/ObservableForProperty/OAPHCreationHelperMixin.cs index a815a57edc..b9b4f494d9 100644 --- a/src/ReactiveUI/ObservableForProperty/OAPHCreationHelperMixin.cs +++ b/src/ReactiveUI/ObservableForProperty/OAPHCreationHelperMixin.cs @@ -1,8 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; +using System.Reactive.Concurrency; +using System.Reflection; + namespace ReactiveUI; /// @@ -14,8 +18,73 @@ namespace ReactiveUI; /// string-based property identification, allow for optional initial values, and provide control over subscription /// timing and notification scheduling. Use these methods to implement read-only reactive properties that automatically /// notify listeners when their values change. +[SuppressMessage( + "Minor Code Smell", + "S101:Types should be named in PascalCase", + Justification = "Established public API; renaming is breaking.")] public static class OAPHCreationHelperMixin { + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, deferSubscription, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// + /// The scheduler that the notifications will be provided on - this should normally + /// be a Dispatcher-based scheduler. + /// + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + IScheduler? scheduler) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, false, scheduler); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -50,8 +119,8 @@ public static ObservableAsPropertyHelper ToProperty( this IObservable target, TObj source, Expression> property, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(target); @@ -61,6 +130,73 @@ public static ObservableAsPropertyHelper ToProperty( return source.ObservableToProperty(target, property, deferSubscription, scheduler); } + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// The initial value of the property. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + TRet initialValue) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, initialValue, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// The initial value of the property. + /// + /// The scheduler that the notifications will be provided on - this should normally + /// be a Dispatcher-based scheduler. + /// + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + TRet initialValue, + IScheduler? scheduler) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, initialValue, false, scheduler); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// The initial value of the property. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + TRet initialValue, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, initialValue, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -99,11 +235,78 @@ public static ObservableAsPropertyHelper ToProperty( TObj source, Expression> property, TRet initialValue, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject => ToProperty(target, source, property, () => initialValue, deferSubscription, scheduler); + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// A function that returns the initial value of the property. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + Func getInitialValue) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, getInitialValue, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// A function that returns the initial value of the property. + /// + /// The scheduler that the notifications will be provided on - this should normally + /// be a Dispatcher-based scheduler. + /// + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + Func getInitialValue, + IScheduler? scheduler) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, getInitialValue, false, scheduler); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// A function that returns the initial value of the property. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + Func getInitialValue, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, getInitialValue, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -142,14 +345,56 @@ public static ObservableAsPropertyHelper ToProperty( TObj source, Expression> property, Func getInitialValue, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(property); return source.ObservableToProperty(target, property, getInitialValue, deferSubscription, scheduler); } + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// An out param matching the return value, provided for convenience. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + out ObservableAsPropertyHelper result) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// An out param matching the return value, provided for convenience. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + out ObservableAsPropertyHelper result, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -188,8 +433,8 @@ public static ObservableAsPropertyHelper ToProperty( TObj source, Expression> property, out ObservableAsPropertyHelper result, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(target); @@ -203,6 +448,52 @@ public static ObservableAsPropertyHelper ToProperty( return ret; } + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// An out param matching the return value, provided for convenience. + /// The initial value of the property. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + out ObservableAsPropertyHelper result, + TRet initialValue) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, initialValue, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// An out param matching the return value, provided for convenience. + /// The initial value of the property. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + out ObservableAsPropertyHelper result, + TRet initialValue, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, initialValue, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -245,11 +536,57 @@ public static ObservableAsPropertyHelper ToProperty( Expression> property, out ObservableAsPropertyHelper result, TRet initialValue, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject => ToProperty(target, source, property, out result, () => initialValue, deferSubscription, scheduler); + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// An out param matching the return value, provided for convenience. + /// A function that returns the initial value of the property. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + out ObservableAsPropertyHelper result, + Func getInitialValue) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, getInitialValue, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// An Expression representing the property (i.e. x => x.SomeProperty). + /// An out param matching the return value, provided for convenience. + /// A function that returns the initial value of the property. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + Expression> property, + out ObservableAsPropertyHelper result, + Func getInitialValue, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, getInitialValue, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -292,8 +629,8 @@ public static ObservableAsPropertyHelper ToProperty( Expression> property, out ObservableAsPropertyHelper result, Func getInitialValue, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(property); @@ -303,6 +640,73 @@ public static ObservableAsPropertyHelper ToProperty( return ret; } + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// The initial value of the property. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + TRet initialValue) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, initialValue, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// The initial value of the property. + /// + /// The scheduler that the notifications will be provided on - this should normally + /// be a Dispatcher-based scheduler. + /// + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + TRet initialValue, + IScheduler? scheduler) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, initialValue, false, scheduler); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// The initial value of the property. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + TRet initialValue, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, initialValue, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -342,11 +746,72 @@ public static ObservableAsPropertyHelper ToProperty( TObj source, string property, TRet initialValue, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject => ToProperty(target, source, property, () => initialValue, deferSubscription, scheduler); + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, deferSubscription, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// + /// The scheduler that the notifications will be provided on - this should normally + /// be a Dispatcher-based scheduler. + /// + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + IScheduler? scheduler) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, false, scheduler); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -382,8 +847,8 @@ public static ObservableAsPropertyHelper ToProperty( this IObservable target, TObj source, string property, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(target); @@ -394,6 +859,48 @@ public static ObservableAsPropertyHelper ToProperty( return source.ObservableToProperty(target, property, deferSubscription, scheduler); } + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// A function that returns the initial value of the property. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + Func getInitialValue) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, getInitialValue, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// A function that returns the initial value of the property. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + Func getInitialValue, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, getInitialValue, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -433,8 +940,8 @@ public static ObservableAsPropertyHelper ToProperty( TObj source, string property, Func getInitialValue, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(target); @@ -445,6 +952,48 @@ public static ObservableAsPropertyHelper ToProperty( return source.ObservableToProperty(target, property, getInitialValue, deferSubscription, scheduler); } + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// An out param matching the return value, provided for convenience. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + out ObservableAsPropertyHelper result) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// An out param matching the return value, provided for convenience. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + out ObservableAsPropertyHelper result, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -483,8 +1032,8 @@ public static ObservableAsPropertyHelper ToProperty( TObj source, string property, out ObservableAsPropertyHelper result, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(target); @@ -497,6 +1046,52 @@ public static ObservableAsPropertyHelper ToProperty( return result; } + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// An out param matching the return value, provided for convenience. + /// A function that returns the initial value of the property. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + out ObservableAsPropertyHelper result, + Func getInitialValue) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, getInitialValue, false, null); + + /// + /// Converts an Observable to an ObservableAsPropertyHelper and + /// automatically provides the onChanged method to raise the property + /// changed notification. + /// + /// The object type. + /// The result type. + /// The observable to convert to an ObservableAsPropertyHelper. + /// The ReactiveObject that has the property. + /// The name of the property. Recommended for use with nameof(). + /// An out param matching the return value, provided for convenience. + /// A function that returns the initial value of the property. + /// If true, defers subscription until the first read of the property value. + /// An initialized ObservableAsPropertyHelper; use this as the backing field for your property. + public static ObservableAsPropertyHelper ToProperty( + this IObservable target, + TObj source, + string property, + out ObservableAsPropertyHelper result, + Func getInitialValue, + bool deferSubscription) + where TObj : class, IReactiveObject => + ToProperty(target, source, property, out result, getInitialValue, deferSubscription, null); + /// /// Converts an Observable to an ObservableAsPropertyHelper and /// automatically provides the onChanged method to raise the property @@ -539,8 +1134,8 @@ public static ObservableAsPropertyHelper ToProperty( string property, out ObservableAsPropertyHelper result, Func getInitialValue, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + bool deferSubscription, + IScheduler? scheduler) where TObj : class, IReactiveObject { ArgumentExceptionHelper.ThrowIfNull(target); @@ -590,26 +1185,32 @@ internal static ObservableAsPropertyHelper ObservableToProperty x.SomeProperty'"); } - var memberInfo = expression.GetMemberInfo() ?? throw new ArgumentException("The property expression does not point towards a valid member.", nameof(property)); + var memberInfo = expression.GetMemberInfo() ?? + throw new ArgumentException( + "The property expression does not point towards a valid member.", + nameof(property)); var name = memberInfo.Name; if (expression is IndexExpression) { name += "[]"; } - return new ObservableAsPropertyHelper( - observable, - _ => target.RaisingPropertyChanged(name), - _ => target.RaisingPropertyChanging(name), - getInitialValue, - deferSubscription, - scheduler); + return new( + observable, + _ => target.RaisingPropertyChanged(name), + _ => target.RaisingPropertyChanging(name), + getInitialValue, + deferSubscription, + scheduler); } /// @@ -646,26 +1247,32 @@ internal static ObservableAsPropertyHelper ObservableToProperty x.SomeProperty'"); } - var memberInfo = expression.GetMemberInfo() ?? throw new ArgumentException("The property expression does not point towards a valid member.", nameof(property)); + var memberInfo = expression.GetMemberInfo() ?? + throw new ArgumentException( + "The property expression does not point towards a valid member.", + nameof(property)); var name = memberInfo.Name; if (expression is IndexExpression) { name += "[]"; } - return new ObservableAsPropertyHelper( - observable, - _ => target.RaisingPropertyChanged(name), - _ => target.RaisingPropertyChanging(name), - () => default, - deferSubscription, - scheduler); + return new( + observable, + _ => target.RaisingPropertyChanged(name), + _ => target.RaisingPropertyChanging(name), + () => default, + deferSubscription, + scheduler); } /// @@ -700,13 +1307,13 @@ internal static ObservableAsPropertyHelper ObservableToProperty( - observable, - _ => target.RaisingPropertyChanged(property), - _ => target.RaisingPropertyChanging(property), - getInitialValue, - deferSubscription, - scheduler); + return new( + observable, + _ => target.RaisingPropertyChanged(property), + _ => target.RaisingPropertyChanging(property), + getInitialValue, + deferSubscription, + scheduler); } /// @@ -738,12 +1345,12 @@ internal static ObservableAsPropertyHelper ObservableToProperty( - observable, - _ => target.RaisingPropertyChanged(property), - _ => target.RaisingPropertyChanging(property), - () => default, - deferSubscription, - scheduler); + return new( + observable, + _ => target.RaisingPropertyChanged(property), + _ => target.RaisingPropertyChanging(property), + () => default, + deferSubscription, + scheduler); } } diff --git a/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs b/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs index 6a5262f4e9..f82255eb75 100644 --- a/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs +++ b/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs @@ -1,8 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; +using System.Reactive.Linq; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -33,157 +38,291 @@ namespace ReactiveUI; /// The type. public sealed class ObservableAsPropertyHelper : IHandleObservableErrors, IDisposable, IEnableLogger { - private readonly Lazy> _thrownExceptions; - private readonly ISubject _subject; + /// Guards the distinct/skip state and the exception observer state. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The scheduler on which value change notifications are delivered. + private readonly IScheduler _scheduler; + + /// Callback invoked before a new value is stored. + private readonly Action _onChanging; + + /// Callback invoked after a new value is stored. + private readonly Action _onChanged; + + /// Function that returns the initial value of the property. private readonly Func _getInitialValue; + + /// Whether leading values equal to the initial value are skipped (deferred subscription). + private readonly bool _skipInitial; + + /// Broadcasts exceptions thrown during property change handling. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _exceptions; + + /// Lazily-created observable wrapper over . + private ExceptionStream? _thrownExceptions; + + /// The most recently produced value from the observable source. private T? _lastValue; - private CompositeDisposable _disposable = []; + + /// The previous value seen by the distinct-until-changed gate (source thread). + private T? _distinctPrevious; + + /// Whether holds a value yet. + private bool _hasDistinctPrevious; + + /// The subscription to the source observable. + private IDisposable? _sourceSubscription; + + /// Flag indicating whether the source observable subscription has been activated. private int _activated; + /// Latched once this helper has been disposed. + private bool _disposed; + + /// + /// Initializes a new instance of the class with no initial value, no deferred subscription, and no scheduler. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged) + : this(observable, onChanged, null, default(T?), false, null) + { + } + + /// + /// Initializes a new instance of the class with a specified initial value. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The initial value of the property. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + T? initialValue) + : this(observable, onChanged, null, initialValue, false, null) + { + } + + /// + /// Initializes a new instance of the class with a specified initial value and scheduler. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The initial value of the property. + /// The scheduler on which change notifications are delivered. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + T? initialValue, + IScheduler? scheduler) + : this(observable, onChanged, null, () => initialValue, false, scheduler) + { + } + + /// + /// Initializes a new instance of the class with a specified initial value and deferred subscription flag. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The initial value of the property. + /// When true, defers source subscription until the first read of Value. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + T? initialValue, + bool deferSubscription) + : this(observable, onChanged, null, initialValue, deferSubscription, null) + { + } + /// /// Initializes a new instance of the class. /// - /// - /// The Observable to base the property on. - /// - /// - /// The action to take when the property changes, typically this will call the - /// ViewModel's RaisePropertyChanged method. - /// - /// - /// The initial value of the property. - /// - /// - /// A value indicating whether the - /// should defer the subscription to the source - /// until the first call to , or if it should immediately - /// subscribe to the source. - /// - /// - /// The scheduler that the notifications will be provided on - - /// this should normally be a Dispatcher-based scheduler. - /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The initial value of the property. + /// When true, defers source subscription until the first read of Value. + /// The scheduler on which change notifications are delivered. public ObservableAsPropertyHelper( IObservable observable, Action onChanged, - T? initialValue = default, - bool deferSubscription = false, - IScheduler? scheduler = null) + T? initialValue, + bool deferSubscription, + IScheduler? scheduler) : this(observable, onChanged, null, initialValue, deferSubscription, scheduler) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with no changing callback, no initial value, no deferred subscription, and no scheduler. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The action called before the property value changes; may be null. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + Action? onChanging) + : this(observable, onChanged, onChanging, () => default, false, null) + { + } + + /// + /// Initializes a new instance of the class with a specified initial value and changing callback. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The action called before the property value changes; may be null. + /// The initial value of the property. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + Action? onChanging, + T? initialValue) + : this(observable, onChanged, onChanging, () => initialValue, false, null) + { + } + + /// + /// Initializes a new instance of the class with changing callback, initial value, and deferred subscription flag. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The action called before the property value changes; may be null. + /// The initial value of the property. + /// When true, defers source subscription until the first read of Value. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + Action? onChanging, + T? initialValue, + bool deferSubscription) + : this(observable, onChanged, onChanging, () => initialValue, deferSubscription, null) + { + } + + /// + /// Initializes a new instance of the class with changing callback, initial value, deferred subscription flag, and scheduler. /// - /// - /// The Observable to base the property on. - /// - /// - /// The action to take when the property changes, typically this will call - /// the ViewModel's RaisePropertyChanged method. - /// - /// - /// The action to take when the property changes, typically this will call - /// the ViewModel's RaisePropertyChanging method. - /// - /// - /// The initial value of the property. - /// - /// - /// A value indicating whether the - /// should defer the subscription to the source - /// until the first call to , or if it should immediately - /// subscribe to the source. - /// - /// - /// The scheduler that the notifications will provided on - this - /// should normally be a Dispatcher-based scheduler. - /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The action called before the property value changes; may be null. + /// The initial value of the property. + /// When true, defers source subscription until the first read of Value. + /// The scheduler on which change notifications are delivered. public ObservableAsPropertyHelper( IObservable observable, Action onChanged, - Action? onChanging = null, - T? initialValue = default, - bool deferSubscription = false, - IScheduler? scheduler = null) // TODO: Create Test + Action? onChanging, + T? initialValue, + bool deferSubscription, + IScheduler? scheduler) : this(observable, onChanged, onChanging, () => initialValue, deferSubscription, scheduler) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with no initial value factory, no deferred subscription, and no scheduler. /// - /// - /// The Observable to base the property on. - /// - /// - /// The action to take when the property changes, typically this will call - /// the ViewModel's RaisePropertyChanged method. - /// - /// - /// The action to take when the property changes, typically this will call - /// the ViewModel's RaisePropertyChanging method. - /// - /// - /// The function used to retrieve the initial value of the property. - /// - /// - /// A value indicating whether the - /// should defer the subscription to the source - /// until the first call to , or if it should immediately - /// subscribe to the source. - /// - /// - /// The scheduler that the notifications will provided on - this - /// should normally be a Dispatcher-based scheduler. - /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The action called before the property value changes; may be null. + /// Factory that returns the initial value; null defaults to returning default(T). public ObservableAsPropertyHelper( IObservable observable, Action onChanged, - Action? onChanging = null, - Func? getInitialValue = null, - bool deferSubscription = false, - IScheduler? scheduler = null) + Action? onChanging, + Func? getInitialValue) + : this(observable, onChanged, onChanging, getInitialValue, false, null) { - ArgumentExceptionHelper.ThrowIfNull(observable); - ArgumentExceptionHelper.ThrowIfNull(onChanged); + } - scheduler ??= CurrentThreadScheduler.Instance; - onChanging ??= _ => { }; + /// + /// Initializes a new instance of the class with an initial value factory and deferred subscription flag. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The action called before the property value changes; may be null. + /// Factory that returns the initial value; null defaults to returning default(T). + /// When true, defers source subscription until the first read of Value. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + Action? onChanging, + Func? getInitialValue, + bool deferSubscription) + : this(observable, onChanged, onChanging, getInitialValue, deferSubscription, null) + { + } - _thrownExceptions = new Lazy>(() => new ScheduledSubject(CurrentThreadScheduler.Instance, RxState.DefaultExceptionHandler)); + /// + /// Initializes a new instance of the class with an initial value factory and deferred subscription flag. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// Factory that returns the initial value; null defaults to returning default(T). + /// When true, defers source subscription until the first read of Value. + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + Func getInitialValue, + bool deferSubscription) + : this(observable, onChanged, null, getInitialValue, deferSubscription, null) + { + } - _subject = new ScheduledSubject(scheduler); - _subject.Subscribe( - x => - { - onChanging(x); - _lastValue = x; - onChanged!(x); - }, - ex => _thrownExceptions.Value.OnNext(ex)) - .DisposeWith(_disposable); + /// + /// Initializes a new instance of the class. + /// + /// The observable to base the property on. + /// The action called when the property value changes. + /// The action called before the property value changes; may be null. + /// Factory that returns the initial value; null defaults to returning default(T). + /// When true, defers source subscription until the first read of Value. + /// The scheduler on which change notifications are delivered. + [SuppressMessage( + "Style", + "IDE0200:Lambda expression can be removed", + Justification = "Method group would force eager Lazy initialization.")] + public ObservableAsPropertyHelper( + IObservable observable, + Action onChanged, + Action? onChanging, + Func? getInitialValue, + bool deferSubscription, + IScheduler? scheduler) + { + ArgumentExceptionHelper.ThrowIfNull(observable); + ArgumentExceptionHelper.ThrowIfNull(onChanged); - _getInitialValue = getInitialValue ??= () => default; + _scheduler = scheduler ?? CurrentThreadScheduler.Instance; + _onChanging = onChanging ?? NoOp; + _onChanged = onChanged; + _getInitialValue = getInitialValue ?? GetDefault; + _skipInitial = deferSubscription; + Source = observable; if (deferSubscription) { - // Although there are no subscribers yet, we should skip all the values that are equal getInitialValue() instead of equal default(T?) because - // default(T?) is never accessible anyway when subscriptions are deferred. We're going to assume that the current value is getInitialValue() even - // if it hasn't been evaluated yet - Source = observable.SkipWhile(x => EqualityComparer.Default.Equals(x, getInitialValue() /* Don't use field to avoid capturing this */)) - .DistinctUntilChanged(); - } - else - { - _lastValue = _getInitialValue(); - Source = observable.StartWith(_lastValue) - .DistinctUntilChanged(); - Source.Subscribe(_subject) - .DisposeWith(_disposable); - _activated = 1; + return; } + + // Eager: seed the initial value (StartWith semantics) and subscribe immediately. + var initial = _getInitialValue(); + _lastValue = initial; + _distinctPrevious = initial; + _hasDistinctPrevious = true; + ScheduleDeliver(initial); + _sourceSubscription = Source.Subscribe(new SourceObserver(this)); + _activated = 1; } /// @@ -193,14 +332,20 @@ public T Value { get { - if (Interlocked.CompareExchange(ref _activated, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _activated, 1, 0) == 0 && !_disposed) { - // Do not subscribe if disposed - var localReferenceInCaseDisposeIsCalled = _disposable; - if (localReferenceInCaseDisposeIsCalled is not null) + _lastValue = _getInitialValue(); + var subscription = Source.Subscribe(new SourceObserver(this)); + lock (_gate) { - _lastValue = _getInitialValue(); - Source.Subscribe(_subject).DisposeWith(localReferenceInCaseDisposeIsCalled); + if (_disposed) + { + subscription.Dispose(); + } + else + { + _sourceSubscription = subscription; + } } } @@ -220,32 +365,196 @@ public T Value /// Gets an observable which signals whenever an exception would normally terminate ReactiveUI /// internal state. /// - public IObservable ThrownExceptions => _thrownExceptions.Value; + public IObservable ThrownExceptions => _thrownExceptions ??= new ExceptionStream(this); + + /// Gets the source observable used to drive this property helper. + internal IObservable Source { get; } - internal /* for testing purposes */ IObservable Source { get; } + /// + /// Constructs a default ObservableAsPropertyHelper with no initial value and no scheduler. + /// + /// A default property helper. + public static ObservableAsPropertyHelper Default() => new(Observable.Never, static _ => { }, default, false, null); /// - /// Constructs a "default" ObservableAsPropertyHelper object. This is - /// useful for when you will initialize the OAPH later, but don't want - /// bindings to access a null OAPH at start up. + /// Constructs a default ObservableAsPropertyHelper with the specified initial value and no scheduler. /// - /// - /// The initial (and only) value of the property. - /// - /// - /// The scheduler that the notifications will be provided on - this should - /// normally be a Dispatcher-based scheduler. - /// + /// The initial (and only) value of the property. /// A default property helper. - public static ObservableAsPropertyHelper Default(T? initialValue = default, IScheduler? scheduler = null) => // TODO: Create Test + public static ObservableAsPropertyHelper Default(T? initialValue) => + new(Observable.Never, static _ => { }, initialValue!, false, null); + + /// + /// Constructs a default ObservableAsPropertyHelper with the specified initial value and scheduler. + /// + /// The initial (and only) value of the property. + /// The scheduler on which change notifications are delivered. + /// A default property helper. + public static ObservableAsPropertyHelper Default(T? initialValue, IScheduler? scheduler) => new(Observable.Never, static _ => { }, initialValue!, false, scheduler); /// /// Disposes this ObservableAsPropertyHelper. /// - public void Dispose() // TODO: Create Test + public void Dispose() + { + lock (_gate) + { + if (_disposed) + { + return; + } + + _disposed = true; + } + + _sourceSubscription?.Dispose(); + } + + /// The no-op onChanging callback used when none is supplied. + /// The unused value. + private static void NoOp(T? value) + { + // Intentionally empty: the default onChanging callback does nothing when the caller supplies none. + } + + /// Returns the default value of . + /// The default value. + private static T? GetDefault() => default; + + /// Schedules delivery of a value's change notifications on the configured scheduler. + /// The value to deliver. + private void ScheduleDeliver(T? value) => + _scheduler.Schedule( + (Helper: this, Value: value), + static (_, state) => + { + state.Helper.Deliver(state.Value); + return EmptyDisposable.Instance; + }); + + /// Runs the onChanging / store / onChanged sequence for a value. + /// The value being delivered. + private void Deliver(T? value) + { + _onChanging(value); + _lastValue = value; + _onChanged(value); + } + + /// Applies the distinct / skip-initial gate to a source value and schedules delivery when it passes. + /// The value produced by the source. + private void OnSourceNext(T? value) + { + lock (_gate) + { + if (_disposed) + { + return; + } + + if (_skipInitial && !_hasDistinctPrevious && EqualityComparer.Default.Equals(value, _getInitialValue())) + { + return; + } + + if (_hasDistinctPrevious && EqualityComparer.Default.Equals(value, _distinctPrevious)) + { + return; + } + + _distinctPrevious = value; + _hasDistinctPrevious = true; + } + + ScheduleDeliver(value); + } + + /// Schedules delivery of a source error to the exceptions stream. + /// The error produced by the source. + private void OnSourceError(Exception error) => + CurrentThreadScheduler.Instance.Schedule( + (Helper: this, Error: error), + static (_, state) => + { + state.Helper.DeliverException(state.Error); + return EmptyDisposable.Instance; + }); + + /// Delivers an exception to subscribers, or to the default handler when there are none. + /// The exception to deliver. + private void DeliverException(Exception error) + { + bool hasObservers; + lock (_gate) + { + hasObservers = _exceptions.HasObservers; + } + + if (hasObservers) + { + _exceptions.Next(error); + return; + } + + RxState.DefaultExceptionHandler.OnNext(error); + } + + /// Adds an exceptions observer. + /// The observer to add. + private void AddException(IObserver observer) + { + lock (_gate) + { + _exceptions.Add(observer); + } + } + + /// Removes an exceptions observer. + /// The observer to remove. + private void RemoveException(IObserver observer) + { + lock (_gate) + { + _exceptions.Remove(observer); + } + } + + /// Routes the source observable's notifications into the helper's gate-guarded state. + /// The owning helper. + private sealed class SourceObserver(ObservableAsPropertyHelper parent) : IObserver + { + /// + public void OnNext(T? value) => parent.OnSourceNext(value); + + /// + public void OnError(Exception error) => parent.OnSourceError(error); + + /// + public void OnCompleted() + { + } + } + + /// The stream. + /// The owning helper. + private sealed class ExceptionStream(ObservableAsPropertyHelper parent) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + parent.AddException(observer); + return new ExceptionSubscription(parent, observer); + } + } + + /// Unsubscribes an exceptions observer on dispose. + /// The owning helper. + /// The subscribed observer. + private sealed class ExceptionSubscription(ObservableAsPropertyHelper parent, IObserver observer) : IDisposable { - _disposable?.Dispose(); - _disposable = null!; + /// + public void Dispose() => parent.RemoveException(observer); } } diff --git a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs index c9bd43b44b..899b9a7419 100644 --- a/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs +++ b/src/ReactiveUI/ObservableForProperty/POCOObservableForProperty.cs @@ -1,11 +1,15 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reactive.Concurrency; using System.Runtime.CompilerServices; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -24,6 +28,8 @@ namespace ReactiveUI; /// repeats the required annotations on its public members to satisfy the interface contract. /// /// +[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Legacy naming convention")] +[SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "Legacy naming convention")] public sealed class POCOObservableForProperty : ICreatesObservableForProperty { /// @@ -34,28 +40,49 @@ public sealed class POCOObservableForProperty : ICreatesObservableForProperty /// private static readonly ConcurrentDictionary<(Type Type, string PropertyName), byte> HasWarned = new(); + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + /// /// /// This fallback returns a very low affinity to ensure it is only used when no more specific implementation applies. /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged) { - ArgumentExceptionHelper.ThrowIfNull(type); ArgumentExceptionHelper.ThrowIfNull(propertyName); - return 1; + return type is null ? 0 : 1; } /// - /// Thrown when is . + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + + /// + /// Thrown when sender is null. [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] public IObservable> GetNotificationForProperty( object sender, Expression expression, string propertyName, - bool beforeChanged = false, - bool suppressWarnings = false) + bool beforeChanged, + bool suppressWarnings) { ArgumentExceptionHelper.ThrowIfNull(sender); ArgumentExceptionHelper.ThrowIfNull(expression); @@ -66,10 +93,9 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang WarnOnce(sender, propertyName); } - // Emit one value, then never complete to preserve legacy WhenAny semantics. - return Observable - .Return(new ObservedChange(sender, expression, default), RxSchedulers.MainThreadScheduler) - .Concat(Observable>.Never); + return new SingleScheduledChange( + new ObservedChange(sender, expression, null), + RxSchedulers.MainThreadScheduler); } /// @@ -84,9 +110,6 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang #endif private void WarnOnce(object sender, string propertyName) { - // Hot path considerations: - // - Avoid ContainsKey + indexer (two lookups). - // - Use TryAdd as the single atomic gate. var type = sender.GetType(); if (!HasWarned.TryAdd((type, propertyName), 0)) { @@ -96,4 +119,28 @@ private void WarnOnce(object sender, string propertyName) this.Log().Warn( $"The class {type.FullName} property {propertyName} is a POCO type and won't send change notifications, WhenAny will only return a single value!"); } + + /// + /// Emits a single observed change on the supplied scheduler and then stays open without completing (a POCO has no + /// further change notifications). Replaces Observable.Return(...).Concat(Never) with one tailored layer. + /// + /// The single observed change to emit. + /// The scheduler the value is emitted on. + private sealed class SingleScheduledChange( + IObservedChange value, + IScheduler scheduler) : IObservable> + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return scheduler.Schedule( + (Observer: observer, Value: value), + static (_, state) => + { + state.Observer.OnNext(state.Value); + return EmptyDisposable.Instance; + }); + } + } } diff --git a/src/ReactiveUI/ObservableFuncMixins.cs b/src/ReactiveUI/ObservableFuncMixins.cs index 66f941bbef..62794cfb94 100644 --- a/src/ReactiveUI/ObservableFuncMixins.cs +++ b/src/ReactiveUI/ObservableFuncMixins.cs @@ -1,8 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -15,6 +19,36 @@ namespace ReactiveUI; /// reflection and may be affected by trimming in certain deployment scenarios. public static class ObservableFuncMixins { + /// + /// Converts a property expression to an observable sequence using default options. + /// + /// The type of the view model. + /// The type of the result. + /// The expression. + /// The view model. + /// An observable sequence of property values. + [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] + public static IObservable ToObservable( + this Expression> expression, + TSource? source) => + expression.ToObservable(source, false, false); + + /// + /// Converts a property expression to an observable sequence, optionally observing values before change. + /// + /// The type of the view model. + /// The type of the result. + /// The expression. + /// The view model. + /// If true, emits the value before the property changes rather than after. + /// An observable sequence of property values. + [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] + public static IObservable ToObservable( + this Expression> expression, + TSource? source, + bool beforeChange) => + expression.ToObservable(source, beforeChange, false); + /// /// Converts to observable. /// @@ -22,8 +56,8 @@ public static class ObservableFuncMixins /// The type of the result. /// The expression. /// The view model. - /// if set to true [before change]. - /// if set to true [skip initial]. + /// If true, emits the value before the property changes rather than after. + /// If true, skips emitting the initial value when subscribing. /// /// An observable Result. /// @@ -31,14 +65,93 @@ public static class ObservableFuncMixins public static IObservable ToObservable( this Expression> expression, TSource? source, - bool beforeChange = false, - bool skipInitial = false) // TODO: Create Test + bool beforeChange, + bool skipInitial) { ArgumentExceptionHelper.ThrowIfNull(expression); var sParam = Reflection.Rewrite(expression.Body); - return source.SubscribeToExpressionChain(sParam, beforeChange, skipInitial, RxSchedulers.SuppressViewCommandBindingMessage) - .Select(static x => x.GetValue()) - .Retry(); + var changes = source.SubscribeToExpressionChain( + sParam, + beforeChange, + skipInitial, + RxSchedulers.SuppressViewCommandBindingMessage); + return new ProjectedRetryObservable(changes); + } + + /// + /// A fused sink that projects each observed change to its current value and retries (resubscribes to the source) on + /// any error — replacing the Select(x => x.GetValue()).Retry() tail of + /// . + /// + /// The type of the observed object. + /// The projected value type. + /// The source stream of observed changes. + [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] + private sealed class ProjectedRetryObservable( + IObservable> source) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(source, observer); + sink.Run(); + return sink; + } + + /// Projects each change via GetValue; on any error, resubscribes to the source (infinite retry). + [RequiresUnreferencedCode("Dynamic observation uses reflection over members that may be trimmed.")] + private sealed class Sink(IObservable> source, IObserver downstream) + : IObserver>, IDisposable + { + /// The current source subscription; reassigned on each retry. + private readonly SwapDisposable _subscription = new(); + + /// Whether this sink has been disposed; stops further retries. + private int _disposed; + + /// Begins observing the source. + public void Run() => _subscription.Disposable = source.Subscribe(this); + + /// + public void OnNext(IObservedChange value) + { + TResult? projected; + try + { + projected = value.GetValue(); + } + catch (Exception ex) + { + OnError(ex); + return; + } + + downstream.OnNext(projected); + } + + /// + public void OnError(Exception error) + { + if (Volatile.Read(ref _disposed) != 0) + { + return; + } + + // Retry semantics: resubscribe to the source instead of forwarding the error. + _subscription.Disposable = source.Subscribe(this); + } + + /// + public void OnCompleted() => downstream.OnCompleted(); + + /// + public void Dispose() + { + Interlocked.Exchange(ref _disposed, 1); + _subscription.Dispose(); + } + } } } diff --git a/src/ReactiveUI/Observables.cs b/src/ReactiveUI/Observables.cs deleted file mode 100644 index f1629aad49..0000000000 --- a/src/ReactiveUI/Observables.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -namespace System.Reactive.Linq; - -/// -/// Provides commonly required, statically-allocated, pre-canned observables. -/// -internal static class Observables -{ - /// - /// An observable that ticks a single, Boolean value of true. - /// - public static readonly IObservable True = Observable.Return(true); - - /// - /// An observable that ticks a single, Boolean value of false. - /// - /// - /// - /// This observable is equivalent to Observable<bool>.Default, but is provided for convenience. - /// - /// - public static readonly IObservable False = Observable.Return(false); - - /// - /// An observable that ticks Unit.Default as a single value. - /// - /// - /// This observable is equivalent to Observable<Unit>.Default, but is provided for convenience. - /// - /// - public static readonly IObservable Unit = Observable.Default; -} diff --git a/src/ReactiveUI/Platforms/android/AndroidCommandBinders.cs b/src/ReactiveUI/Platforms/android/AndroidCommandBinders.cs index 7f423afd9b..03d757eef3 100644 --- a/src/ReactiveUI/Platforms/android/AndroidCommandBinders.cs +++ b/src/ReactiveUI/Platforms/android/AndroidCommandBinders.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Reflection; - using Android.Views; namespace ReactiveUI; @@ -15,6 +14,11 @@ namespace ReactiveUI; [Preserve(AllMembers = true)] public sealed class AndroidCommandBinders : FlexibleCommandBinder { + /// + /// The binding affinity used when registering the command binding. + /// + private const int ViewClickBindingAffinity = 9; + /// /// Initializes a new instance of the class. /// @@ -25,28 +29,24 @@ public AndroidCommandBinders() { var viewType = typeof(View); - // Cache reflection metadata once at registration time. var enabledProperty = viewType.GetRuntimeProperty("Enabled") ?? throw new InvalidOperationException( "Could not find property 'Enabled' on type View, which is needed for binding"); - // Precompute the setter once; ForEvent will no-op enabled sync if null (but for View it should exist). var enabledSetter = Reflection.GetValueSetterForProperty(enabledProperty); - Register(viewType, 9, (cmd, target, commandParameter) => + Register(viewType, ViewClickBindingAffinity, (cmd, target, commandParameter) => { - // Keep existing behavior: ForEvent throws if cmd is null. - // Also keep the "null commandParameter means use target" idiom (handled inside ForEvent overload). var view = (View)target!; return ForEvent( cmd, view, commandParameter, - addHandler: h => view.Click += h, - removeHandler: h => view.Click -= h, - enabledSetter: enabledSetter); + h => view.Click += h, + h => view.Click -= h, + enabledSetter); }); } } diff --git a/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs b/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs index 033ea67fe0..aee978b440 100644 --- a/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs +++ b/src/ReactiveUI/Platforms/android/AndroidObservableForWidgets.cs @@ -1,18 +1,19 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Frozen; -using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Reactive.Linq; +using System.Reflection; using System.Runtime.Versioning; - using Android.OS; using Android.Text; using Android.Views; using Android.Widget; -using Observable = System.Reactive.Linq.Observable; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -35,13 +36,27 @@ namespace ReactiveUI; [Preserve(AllMembers = true)] public sealed class AndroidObservableForWidgets : ICreatesObservableForProperty { + /// + /// The Android API level (23, Marshmallow) at which and + /// were introduced, replacing the obsolete CurrentHour/CurrentMinute members. + /// + private const int TimePickerHourMinuteApiLevel = 23; + + /// + /// The initial capacity used for the per-property list of candidate widget types. + /// + private const int CandidateTypesInitialCapacity = 2; + /// /// Stores observable factory functions keyed by (widget type, property name). /// /// /// This table is immutable after type initialization and is safe for concurrent reads. /// - private static readonly FrozenDictionary<(Type ViewType, string PropertyName), Func>>> DispatchTable; + private static readonly + FrozenDictionary< + (Type ViewType, string PropertyName), + Func>>> DispatchTable; /// /// Stores, per property name, the set of widget types that can produce notifications for that property. @@ -68,43 +83,34 @@ static AndroidObservableForWidgets() static v => v.Text, static (v, h) => v.TextChanged += h, static (v, h) => v.TextChanged -= h), - CreateFromWidget( static v => v.Value, static (v, h) => v.ValueChanged += h, static (v, h) => v.ValueChanged -= h), - CreateFromWidget( static v => v.Rating, static (v, h) => v.RatingBarChange += h, static (v, h) => v.RatingBarChange -= h), - CreateFromWidget( static v => v.Checked, static (v, h) => v.CheckedChange += h, static (v, h) => v.CheckedChange -= h), - CreateFromWidget( static v => v.Date, static (v, h) => v.DateChange += h, static (v, h) => v.DateChange -= h), - CreateFromWidget( static v => v.CurrentTab, static (v, h) => v.TabChanged += h, static (v, h) => v.TabChanged -= h), - - CreateTimePickerHourFromWidget(), - CreateTimePickerMinuteFromWidget(), - CreateFromAdapterView(), + CreateTimePickerHourFromWidget(), CreateTimePickerMinuteFromWidget(), CreateFromAdapterView() }; - var dispatch = - new Dictionary<(Type ViewType, string PropertyName), Func>>>( - capacity: items.Length); + Dictionary<(Type ViewType, string PropertyName), Func>>> dispatch = + new( + items.Length); - var byProperty = - new Dictionary>(capacity: items.Length, comparer: StringComparer.Ordinal); + Dictionary> byProperty = new(items.Length, StringComparer.Ordinal); for (var i = 0; i < items.Length; i++) { @@ -119,7 +125,7 @@ static AndroidObservableForWidgets() if (!byProperty.TryGetValue(item.Property, out var list)) { - list = new List(capacity: 2); + list = new(CandidateTypesInitialCapacity); byProperty.Add(item.Property, list); } @@ -128,24 +134,29 @@ static AndroidObservableForWidgets() DispatchTable = dispatch.ToFrozenDictionary(); - var index = new Dictionary(byProperty.Count, StringComparer.Ordinal); + Dictionary index = new(byProperty.Count, StringComparer.Ordinal); foreach (var pair in byProperty) { - index[pair.Key] = pair.Value.ToArray(); + index[pair.Key] = [.. pair.Value]; } TypesByPropertyName = index.ToFrozenDictionary(StringComparer.Ordinal); } + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + /// /// /// This implementation does not support before-change notifications. /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type? type, string propertyName, bool beforeChanged) { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(propertyName); + ArgumentExceptionHelper.ThrowIfNull(type); + ArgumentExceptionHelper.ThrowIfNull(propertyName); if (beforeChanged) { @@ -161,43 +172,59 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang { if (candidates[i].IsAssignableFrom(type)) { - return 5; + return BindingAffinity.Explicit; } } return 0; } + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + /// /// /// This implementation does not support before-change notifications. /// /// - /// Thrown when , , or is - /// . + /// Thrown when sender, expression, or propertyName is null. /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] public IObservable> GetNotificationForProperty( object sender, Expression expression, string propertyName, - bool beforeChanged = false, - bool suppressWarnings = false) + bool beforeChanged, + bool suppressWarnings) { - ArgumentNullException.ThrowIfNull(sender); - ArgumentNullException.ThrowIfNull(expression); - ArgumentNullException.ThrowIfNull(propertyName); + ArgumentExceptionHelper.ThrowIfNull(sender); + ArgumentExceptionHelper.ThrowIfNull(expression); + ArgumentExceptionHelper.ThrowIfNull(propertyName); if (beforeChanged) { - return Observable.Never>(); + return Observable>.Never; } var senderType = sender.GetType(); if (!TypesByPropertyName.TryGetValue(propertyName, out var candidates)) { - return Observable.Never>(); + return Observable>.Never; } for (var i = 0; i < candidates.Length; i++) @@ -211,10 +238,10 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang return DispatchTable.TryGetValue((candidateType, propertyName), out var factory) ? factory(sender, expression) - : Observable.Never>(); + : Observable>.Never; } - return Observable.Never>(); + return Observable>.Never; } /// @@ -231,39 +258,10 @@ private static DispatchItem CreateFromAdapterView() { const string propName = "SelectedItem"; - return new DispatchItem( + return new( typeof(AdapterView), propName, - (x, ex) => - { - var adapterView = (AdapterView)x; - - var itemSelected = - Observable.FromEvent, ObservedChange>( - eventHandler => - { - void Handler(object? unusedSender, AdapterView.ItemSelectedEventArgs unusedEventArgs) => - eventHandler(new ObservedChange(adapterView, ex, default)); - - return Handler; - }, - h => adapterView.ItemSelected += h, - h => adapterView.ItemSelected -= h); - - var nothingSelected = - Observable.FromEvent, ObservedChange>( - eventHandler => - { - void Handler(object? unusedSender, AdapterView.NothingSelectedEventArgs unusedEventArgs) => - eventHandler(new ObservedChange(adapterView, ex, default)); - - return Handler; - }, - h => adapterView.NothingSelected += h, - h => adapterView.NothingSelected -= h); - - return itemSelected.Merge(nothingSelected); - }); + (x, ex) => new AdapterSelectionObservable((AdapterView)x, ex)); } /// @@ -278,7 +276,7 @@ void Handler(object? unusedSender, AdapterView.NothingSelectedEventArgs unusedEv [SupportedOSPlatform("android23.0")] private static DispatchItem CreateTimePickerHourFromWidget() { - if ((int)Build.VERSION.SdkInt >= 23) + if ((int)Build.VERSION.SdkInt >= TimePickerHourMinuteApiLevel) { return CreateFromWidget( static v => v.Hour, @@ -304,7 +302,7 @@ private static DispatchItem CreateTimePickerHourFromWidget() [SupportedOSPlatform("android23.0")] private static DispatchItem CreateTimePickerMinuteFromWidget() { - if ((int)Build.VERSION.SdkInt >= 23) + if ((int)Build.VERSION.SdkInt >= TimePickerHourMinuteApiLevel) { return CreateFromWidget( static v => v.Minute, @@ -348,27 +346,77 @@ private static DispatchItem CreateFromWidget( property.Body.GetMemberInfo() ?? throw new ArgumentException("Does not have a valid body member info.", nameof(property)); - // ExpressionToPropertyNames is used here as it handles boxing expressions that might occur due to our use of object. var propName = memberInfo.Name; - return new DispatchItem( + return new( typeof(TView), propName, - (x, ex) => + (x, ex) => new WidgetEventObservable((TView)x, ex, addHandler, removeHandler)); + } + + /// + /// Bridges an 's selection events into an observed-change stream — replacing the + /// FromEvent(ItemSelected).Merge(FromEvent(NothingSelected)) chain. Each + /// or raise emits an observed change; both handlers are detached on dispose. + /// + /// The adapter view to observe. + /// The expression surfaced on the emitted change. + private sealed class AdapterSelectionObservable(AdapterView adapterView, Expression expression) + : IObservable> + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + void OnItemSelected(object? sender, AdapterView.ItemSelectedEventArgs args) => + observer.OnNext(new ObservedChange(adapterView, expression, null)); + + void OnNothingSelected(object? sender, AdapterView.NothingSelectedEventArgs args) => + observer.OnNext(new ObservedChange(adapterView, expression, null)); + + adapterView.ItemSelected += OnItemSelected; + adapterView.NothingSelected += OnNothingSelected; + + return new ActionDisposable(() => { - var view = (TView)x; - - return Observable.FromEvent, ObservedChange>( - eventHandler => - { - void Handler(object? unusedSender, TEventArgs unusedEventArgs) => - eventHandler(new ObservedChange(view, ex, default)); - - return Handler; - }, - h => addHandler(view, h), - h => removeHandler(view, h)); + adapterView.ItemSelected -= OnItemSelected; + adapterView.NothingSelected -= OnNothingSelected; }); + } + } + + /// + /// Bridges a widget's typed CLR event into an observed-change stream — replacing + /// Observable.FromEvent. Each event raise emits an for the + /// widget; the handler is detached when the subscription is disposed. + /// + /// The widget type. + /// The widget event's argument type. + /// The widget instance. + /// The expression surfaced on the emitted change. + /// Attaches an event handler to the widget. + /// Detaches an event handler from the widget. + private sealed class WidgetEventObservable( + TView view, + Expression expression, + Action> addHandler, + Action> removeHandler) + : IObservable> + where TView : View + where TEventArgs : EventArgs + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + void Handler(object? sender, TEventArgs args) => + observer.OnNext(new ObservedChange(view!, expression, null)); + + addHandler(view, Handler); + return new ActionDisposable(() => removeHandler(view, Handler)); + } } /// diff --git a/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs b/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs index 8e07601702..266fb1b8db 100644 --- a/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs +++ b/src/ReactiveUI/Platforms/android/AutoSuspendHelper.cs @@ -1,10 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using Android.App; -using Android.OS; +using System.Reactive; +using System.Reactive.Subjects; + +using ReactiveUI.Internal; namespace ReactiveUI; @@ -46,39 +48,51 @@ namespace ReactiveUI; /// public class AutoSuspendHelper : IEnableLogger, IDisposable { - private readonly Subject _onCreate = new(); - private readonly Subject _onRestart = new(); - private readonly Subject _onPause = new(); - private readonly Subject _onSaveInstanceState = new(); + /// Relays Activity create callbacks along with the saved-state bundle. + private readonly BroadcastSubject _onCreate = new(); + + /// Relays Activity resume callbacks. + private readonly BroadcastSubject _onRestart = new(); + + /// Relays Activity pause callbacks. + private readonly BroadcastSubject _onPause = new(); + + /// Relays Activity save-instance-state callbacks along with the out bundle. + private readonly BroadcastSubject _onSaveInstanceState = new(); - private bool _disposedValue; // To detect redundant calls + /// Tracks whether this instance has already been disposed. + private bool _disposedValue; /// /// Initializes static members of the class. /// - static AutoSuspendHelper() => AppDomain.CurrentDomain.UnhandledException += static (o, e) => UntimelyDemise.OnNext(Unit.Default); + static AutoSuspendHelper() => AppDomain.CurrentDomain.UnhandledException += + static (_, _) => UntimelyDemise.OnNext(Unit.Default); /// /// Initializes a new instance of the class. /// /// The host application. - public AutoSuspendHelper(Application hostApplication) // TODO: Create Test + public AutoSuspendHelper(Application hostApplication) { - hostApplication?.RegisterActivityLifecycleCallbacks(new ObservableLifecycle(this)); + ArgumentExceptionHelper.ThrowIfNull(hostApplication); + hostApplication.RegisterActivityLifecycleCallbacks(new ObservableLifecycle(this)); - _onCreate.Merge(_onSaveInstanceState).Subscribe(static x => LatestBundle = x); + // Both create and save-instance-state callbacks update the latest bundle (replaces a Merge + Subscribe). + _onCreate.Subscribe(new DelegateObserver(static x => LatestBundle = x)); + _onSaveInstanceState.Subscribe(new DelegateObserver(static x => LatestBundle = x)); - RxSuspension.SuspensionHost.IsLaunchingNew = _onCreate.Where(static x => x is null).Select(static _ => Unit.Default); - RxSuspension.SuspensionHost.IsResuming = _onCreate.Where(static x => x is not null).Select(static _ => Unit.Default); + RxSuspension.SuspensionHost.IsLaunchingNew = new CreateSignalObservable(_onCreate, emitWhenNull: true); + RxSuspension.SuspensionHost.IsResuming = new CreateSignalObservable(_onCreate, emitWhenNull: false); RxSuspension.SuspensionHost.IsUnpausing = _onRestart; - RxSuspension.SuspensionHost.ShouldPersistState = _onPause.Select(static _ => Disposable.Empty); + RxSuspension.SuspensionHost.ShouldPersistState = new PersistSignalObservable(_onPause); RxSuspension.SuspensionHost.ShouldInvalidateState = UntimelyDemise; } /// /// Gets a subject to indicate whether the application has untimely dismissed. /// - public static Subject UntimelyDemise { get; } = new(); + public static ISubject UntimelyDemise { get; } = new BroadcastSubject(); /// /// Gets or sets the latest bundle. @@ -93,7 +107,6 @@ public AutoSuspendHelper(Application hostApplication) // TODO: Create Test /// public void Dispose() { - // Do not change this code. Put clean up code in Dispose(bool disposing) above. Dispose(true); GC.SuppressFinalize(this); } @@ -111,15 +124,88 @@ protected virtual void Dispose(bool disposing) if (disposing) { - _onCreate?.Dispose(); - _onPause?.Dispose(); - _onRestart?.Dispose(); - _onSaveInstanceState?.Dispose(); + _onCreate.Dispose(); + _onPause.Dispose(); + _onRestart.Dispose(); + _onSaveInstanceState.Dispose(); } _disposedValue = true; } + /// + /// Emits for each create callback whose saved-state bundle matches the requested + /// null-ness — replacing _onCreate.Where(x => x is null/not null).Select(_ => Unit.Default). + /// + /// The create-callback stream carrying the saved-state bundle. + /// When true, emits for null bundles (cold launch); when false, for non-null bundles (resume). + private sealed class CreateSignalObservable(IObservable source, bool emitWhenNull) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, emitWhenNull)); + } + + /// + /// Emits for each create callback whose saved-state bundle matches the requested + /// null-ness. + /// + /// The observer to receive the notifications. + /// When true, emits for null bundles (cold launch); when false, for non-null bundles (resume). + private sealed class Sink(IObserver downstream, bool emitWhenNull) : IObserver + { + /// + public void OnNext(Bundle? value) + { + if ((value is null) != emitWhenNull) + { + return; + } + + downstream.OnNext(Unit.Default); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// + /// Emits an empty disposable for each pause callback — replacing _onPause.Select(_ => Disposable.Empty) + /// to feed . + /// + /// The pause-callback stream. + private sealed class PersistSignalObservable(IObservable source) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// + /// Emits an empty disposable for each pause callback. + /// + /// The downstream observer. + private sealed class Sink(IObserver downstream) : IObserver + { + /// + public void OnNext(Unit value) => downstream.OnNext(EmptyDisposable.Instance); + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + /// /// Handles Android activity lifecycle events and forwards them to the associated AutoSuspendHelper instance for /// reactive processing. @@ -128,31 +214,38 @@ protected virtual void Dispose(bool disposing) /// activity lifecycle changes. It is intended for internal use to bridge Android lifecycle events to reactive /// streams managed by AutoSuspendHelper. /// The AutoSuspendHelper instance that receives lifecycle event notifications. - private class ObservableLifecycle(AutoSuspendHelper @this) : Java.Lang.Object, Application.IActivityLifecycleCallbacks + private sealed class ObservableLifecycle(AutoSuspendHelper @this) + : Java.Lang.Object, Application.IActivityLifecycleCallbacks { - public void OnActivityCreated(Activity? activity, Bundle? savedInstanceState) => @this._onCreate.OnNext(savedInstanceState); + /// + public void OnActivityCreated(Activity? activity, Bundle? savedInstanceState) => + @this._onCreate.OnNext(savedInstanceState); + /// public void OnActivityResumed(Activity? activity) => @this._onRestart.OnNext(Unit.Default); + /// public void OnActivitySaveInstanceState(Activity? activity, Bundle? outState) { - // NB: This is so that we always have a bundle on OnCreate, so that - // we can tell the difference between created from scratch and resume. outState?.PutString("___dummy_value_please_create_a_bundle", "VeryYes"); @this._onSaveInstanceState.OnNext(outState); } + /// public void OnActivityPaused(Activity? activity) => @this._onPause.OnNext(Unit.Default); + /// public void OnActivityDestroyed(Activity? activity) { } + /// public void OnActivityStarted(Activity? activity) { } + /// public void OnActivityStopped(Activity? activity) { } diff --git a/src/ReactiveUI/Platforms/android/BundleSuspensionDriver.cs b/src/ReactiveUI/Platforms/android/BundleSuspensionDriver.cs index 62406bc5ce..5abbef728d 100644 --- a/src/ReactiveUI/Platforms/android/BundleSuspensionDriver.cs +++ b/src/ReactiveUI/Platforms/android/BundleSuspensionDriver.cs @@ -1,12 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.IO; +using System.Reactive; using System.Text.Json; using System.Text.Json.Serialization.Metadata; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -20,6 +22,9 @@ namespace ReactiveUI; /// public sealed class BundleSuspensionDriver : ISuspensionDriver { + /// + /// The bundle key under which the serialized application state is stored. + /// private const string StateKey = "__state"; /// @@ -33,103 +38,106 @@ public sealed class BundleSuspensionDriver : ISuspensionDriver { try { - // NB: Sometimes OnCreate gives us a null bundle. if (AutoSuspendHelper.LatestBundle is null) { - return Observable.Throw( + return new ThrowObservable( new InvalidOperationException("New bundle detected; no persisted state is available.")); } var buffer = AutoSuspendHelper.LatestBundle.GetByteArray(StateKey); if (buffer is null) { - return Observable.Throw( + return new ThrowObservable( new InvalidOperationException("The persisted state buffer could not be found.")); } - return Observable.FromAsync(async () => + return new TaskObservable(DeserializeAsync()); + + async Task DeserializeAsync() { - await using var stream = new MemoryStream(buffer, writable: false); + await using MemoryStream stream = new(buffer, false); return await JsonSerializer.DeserializeAsync(stream).ConfigureAwait(false); - }); - } - catch (Exception ex) - { - return Observable.Throw(ex); - } - } - - /// - [RequiresUnreferencedCode( - "Implementations commonly use reflection-based serialization. " + - "Prefer SaveState(T, JsonTypeInfo) for trimming or AOT scenarios.")] - [RequiresDynamicCode( - "Implementations commonly use reflection-based serialization. " + - "Prefer SaveState(T, JsonTypeInfo) for trimming or AOT scenarios.")] - public IObservable SaveState(T state) - { - try - { - using var stream = new MemoryStream(); - JsonSerializer.Serialize(stream, state); - - AutoSuspendHelper.LatestBundle?.PutByteArray(StateKey, stream.ToArray()); - return Observables.Unit; + } } catch (Exception ex) { - return Observable.Throw(ex); + return new ThrowObservable(ex); } } /// public IObservable LoadState(JsonTypeInfo typeInfo) { - ArgumentNullException.ThrowIfNull(typeInfo); + ArgumentExceptionHelper.ThrowIfNull(typeInfo); try { if (AutoSuspendHelper.LatestBundle is null) { - return Observable.Throw( + return new ThrowObservable( new InvalidOperationException("New bundle detected; no persisted state is available.")); } var buffer = AutoSuspendHelper.LatestBundle.GetByteArray(StateKey); if (buffer is null) { - return Observable.Throw( + return new ThrowObservable( new InvalidOperationException("The persisted state buffer could not be found.")); } - return Observable.FromAsync(async () => + return new TaskObservable(DeserializeAsync()); + + async Task DeserializeAsync() { - await using var stream = new MemoryStream(buffer, writable: false); + await using MemoryStream stream = new(buffer, false); return await JsonSerializer.DeserializeAsync(stream, typeInfo).ConfigureAwait(false); - }); + } + } + catch (Exception ex) + { + return new ThrowObservable(ex); + } + } + + /// + [RequiresUnreferencedCode( + "Implementations commonly use reflection-based serialization. " + + "Prefer SaveState(T, JsonTypeInfo) for trimming or AOT scenarios.")] + [RequiresDynamicCode( + "Implementations commonly use reflection-based serialization. " + + "Prefer SaveState(T, JsonTypeInfo) for trimming or AOT scenarios.")] + public IObservable SaveState(T state) + { + try + { + using MemoryStream stream = new(); + JsonSerializer.Serialize(stream, state); + + AutoSuspendHelper.LatestBundle?.PutByteArray(StateKey, stream.ToArray()); + return SingleValueObservable.Unit; } catch (Exception ex) { - return Observable.Throw(ex); + return new ThrowObservable(ex); } } /// public IObservable SaveState(T state, JsonTypeInfo typeInfo) { - ArgumentNullException.ThrowIfNull(typeInfo); + ArgumentExceptionHelper.ThrowIfNull(typeInfo); try { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); JsonSerializer.Serialize(stream, state, typeInfo); AutoSuspendHelper.LatestBundle?.PutByteArray(StateKey, stream.ToArray()); - return Observables.Unit; + return SingleValueObservable.Unit; } catch (Exception ex) { - return Observable.Throw(ex); + return new ThrowObservable(ex); } } @@ -139,11 +147,11 @@ public IObservable InvalidateState() try { AutoSuspendHelper.LatestBundle?.PutByteArray(StateKey, []); - return Observables.Unit; + return SingleValueObservable.Unit; } catch (Exception ex) { - return Observable.Throw(ex); + return new ThrowObservable(ex); } } } diff --git a/src/ReactiveUI/Platforms/android/ContextExtensions.cs b/src/ReactiveUI/Platforms/android/ContextExtensions.cs index 6e3944e068..06e3e59172 100644 --- a/src/ReactiveUI/Platforms/android/ContextExtensions.cs +++ b/src/ReactiveUI/Platforms/android/ContextExtensions.cs @@ -1,11 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.Content; using Android.OS; - using Context = Android.Content.Context; namespace ReactiveUI; @@ -20,13 +19,30 @@ namespace ReactiveUI; public static class ContextExtensions { /// - /// Binds the service. + /// Binds the service and exposes the service binder as an observable sequence. + /// + /// The observable sequence of service binders. + /// The Context to bind the Service from. + /// + /// Identifies the service to connect to. The Intent may specify either an explicit component name, + /// or a logical description (action, category, etc) to match an IntentFilter published by a service. + /// + public static IObservable + ServiceBound(this Context context, Intent intent) => + ServiceBound(context, intent, Bind.None); + + /// + /// Binds the service and exposes the service binder as an observable sequence. /// /// The observable sequence of service binders. /// The Context to bind the Service from. - /// Identifies the service to connect to. The Intent may specify either an explicit component name, or a logical description (action, category, etc) to match an IntentFilter published by a service. - /// Operation options for the binding. The default is Bind.None. - public static IObservable ServiceBound(this Context context, Intent intent, Bind flags = Bind.None) => // TODO: Create Test + /// + /// Identifies the service to connect to. The Intent may specify either an explicit component name, + /// or a logical description (action, category, etc) to match an IntentFilter published by a service. + /// + /// Operation options for the binding. + public static IObservable + ServiceBound(this Context context, Intent intent, Bind flags) => ServiceBound(context, intent, flags); /// @@ -34,19 +50,68 @@ public static class ContextExtensions /// /// The observable sequence of service binders. /// The Context to bind the Service from. - /// Identifies the service to connect to. The Intent may specify either an explicit component name, or a logical description (action, category, etc) to match an IntentFilter published by a service. - /// Operation options for the binding. The default is Bind.None. + /// + /// Identifies the service to connect to. The Intent may specify either an explicit component name, + /// or a logical description (action, category, etc) to match an IntentFilter published by a service. + /// /// The type of the returned service binder. - public static IObservable ServiceBound(this Context context, Intent intent, Bind flags = Bind.None) // TODO: Create Test - where TBinder : class, IBinder => - Observable.Create(observer => + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IObservable ServiceBound( + this Context context, + Intent intent) + where TBinder : class, IBinder + => + ServiceBound(context, intent, Bind.None); + + /// + /// Binds the service. + /// + /// The observable sequence of service binders. + /// The Context to bind the Service from. + /// + /// Identifies the service to connect to. The Intent may specify either an explicit component name, + /// or a logical description (action, category, etc) to match an IntentFilter published by a service. + /// + /// Operation options for the binding. + /// The type of the returned service binder. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static IObservable ServiceBound( + this Context context, + Intent intent, + Bind flags) + where TBinder : class, IBinder + => + new ServiceBoundObservable(context, intent, flags); + + /// + /// Binds the service on subscribe and surfaces its binder through the observer — replacing + /// Observable.Create. The returned unbinds the service on dispose. + /// + /// The binder type. + /// The context used to bind and unbind the service. + /// The intent identifying the service to bind. + /// The bind flags. + private sealed class ServiceBoundObservable(Context context, Intent intent, Bind flags) + : IObservable + where TBinder : class, IBinder + { + /// + public IDisposable Subscribe(IObserver observer) { - var connection = new ServiceConnection(context, observer); + ArgumentExceptionHelper.ThrowIfNull(observer); + + ServiceConnection connection = new(context, observer); try { if (!context.BindService(intent, connection, flags)) { - observer.OnError(new Exception("Service bind failed!")); + observer.OnError(new InvalidOperationException("Service bind failed!")); } } catch (Exception ex) @@ -55,29 +120,40 @@ public static class ContextExtensions } return connection; - }); + } + } /// /// A private implementation of IServiceConnection and IDisposable. /// /// The binder type. - private class ServiceConnection(Context context, IObserver observer) : Java.Lang.Object, IServiceConnection + private sealed class ServiceConnection(Context context, IObserver observer) + : Java.Lang.Object, IServiceConnection where TBinder : class, IBinder { + /// + /// The Context used to bind and unbind the service. + /// private readonly Context _context = context; + + /// + /// The observer that receives the service binder notifications. + /// private readonly IObserver _observer = observer; + /// + /// Indicates whether this instance has already been disposed. + /// private bool _disposed; -#pragma warning disable RCS1168 // Parameter name differs from base name. - void IServiceConnection.OnServiceConnected(ComponentName? name, IBinder? binder) => _observer.OnNext((TBinder?)binder); -#pragma warning restore RCS1168 // Parameter name differs from base name. - - void IServiceConnection.OnServiceDisconnected(ComponentName? name) => + /// + void IServiceConnection.OnServiceConnected(ComponentName? name, IBinder? binder) => + _observer.OnNext((TBinder?)binder); - // lost connection to the remote service but it may be revived - _observer.OnNext(null); + /// + void IServiceConnection.OnServiceDisconnected(ComponentName? name) => _observer.OnNext(null); + /// protected override void Dispose(bool disposing) { if (!_disposed && disposing) diff --git a/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs b/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs index 2f90bcb5c8..a73ed93941 100644 --- a/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs +++ b/src/ReactiveUI/Platforms/android/ControlFetcherMixin.cs @@ -1,20 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -#nullable enable - using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; - using Android.App; using Android.Views; -using ReactiveUI.Helpers; - namespace ReactiveUI; /// @@ -53,7 +47,11 @@ public static partial class ControlFetcherMixin /// to avoid race conditions if called from multiple threads (e.g., during tests or unusual scheduling). /// /// +#if NET8_0_OR_GREATER + private static readonly ConditionalWeakTable> ViewCache = []; +#else private static readonly ConditionalWeakTable> ViewCache = new(); +#endif /// /// Cache of wire-up property lists per runtime type and resolve strategy. @@ -61,7 +59,8 @@ public static partial class ControlFetcherMixin /// /// This avoids repeated reflection over properties when wiring up controls multiple times. /// - private static readonly ConcurrentDictionary<(Type Type, ResolveStrategy Strategy), PropertyInfo[]> WireUpMembersCache = new(); + private static readonly ConcurrentDictionary<(Type Type, ResolveStrategy Strategy), PropertyInfo[]> + WireUpMembersCache = new(); /// /// Gets a control from an using the calling member name as the default resource name. @@ -71,7 +70,8 @@ public static partial class ControlFetcherMixin /// The property name to use as the resource identifier. Defaults to the calling member name. /// /// The resolved view if found; otherwise . - [RequiresUnreferencedCode("Android resource discovery uses reflection over generated resource types that may be trimmed.")] + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] [RequiresDynamicCode("Android resource discovery uses reflection that may require dynamic code generation.")] public static View? GetControl(this Activity activity, [CallerMemberName] string? propertyName = null) => GetCachedControl( @@ -93,7 +93,8 @@ public static partial class ControlFetcherMixin /// The property name to use as the resource identifier. Defaults to the calling member name. /// /// The resolved view if found; otherwise . - [RequiresUnreferencedCode("Android resource discovery uses reflection over generated resource types that may be trimmed.")] + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] [RequiresDynamicCode("Android resource discovery uses reflection that may require dynamic code generation.")] public static View? GetControl(this View view, Assembly assembly, [CallerMemberName] string? propertyName = null) => GetCachedControl( @@ -107,6 +108,20 @@ public static partial class ControlFetcherMixin }, assembly); + /// + /// Wires view controls to properties on an using the implicit resolve strategy. + /// + /// The layout host that exposes a . + /// Thrown when is null. + /// + /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. + /// + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] + public static void WireUpControls(this ILayoutViewHost layoutHost) => + WireUpControls(layoutHost, ResolveStrategy.Implicit); + /// /// Wires view controls to properties on an . /// @@ -116,9 +131,12 @@ public static partial class ControlFetcherMixin /// /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. /// - [RequiresUnreferencedCode("WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] - public static void WireUpControls(this ILayoutViewHost layoutHost, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) + public static void WireUpControls( + this ILayoutViewHost layoutHost, + ResolveStrategy resolveMembers) { ArgumentExceptionHelper.ThrowIfNull(layoutHost); @@ -134,22 +152,33 @@ public static void WireUpControls(this ILayoutViewHost layoutHost, ResolveStrate var root = layoutHost.View; var resourceName = member.GetResourceName(); - var resolved = root is null - ? null - : root.GetControl(hostType.Assembly, resourceName); + var resolved = root?.GetControl(hostType.Assembly, resourceName); member.SetValue(layoutHost, resolved); } catch (Exception ex) { throw new MissingFieldException( - "Failed to wire up the Property " + member.Name + - " to a View in your layout with a corresponding identifier.", + $"Failed to wire up the Property {member.Name} to a View in your layout with a corresponding identifier.", ex); } } } + /// + /// Wires view controls to properties on an Android using the implicit resolve strategy. + /// + /// The view whose properties should be wired. + /// Thrown when is null. + /// + /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. + /// + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] + public static void WireUpControls(this View view) => + WireUpControls(view, ResolveStrategy.Implicit); + /// /// Wires view controls to properties on an Android . /// @@ -159,9 +188,10 @@ public static void WireUpControls(this ILayoutViewHost layoutHost, ResolveStrate /// /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. /// - [RequiresUnreferencedCode("WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] - public static void WireUpControls(this View view, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) + public static void WireUpControls(this View view, ResolveStrategy resolveMembers) { ArgumentExceptionHelper.ThrowIfNull(view); @@ -189,6 +219,23 @@ public static void WireUpControls(this View view, ResolveStrategy resolveMembers } } + /// + /// Wires view controls to properties on an Android using the implicit resolve strategy. + /// + /// The fragment whose properties should be wired. + /// The inflated view returned from OnCreateView. + /// + /// Thrown when or is null. + /// + /// + /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. + /// + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] + public static void WireUpControls(this Fragment fragment, View inflatedView) => + WireUpControls(fragment, inflatedView, ResolveStrategy.Implicit); + /// /// Wires view controls to properties on an Android . /// @@ -201,9 +248,13 @@ public static void WireUpControls(this View view, ResolveStrategy resolveMembers /// /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. /// - [RequiresUnreferencedCode("WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] - public static void WireUpControls(this Fragment fragment, View inflatedView, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) + public static void WireUpControls( + this Fragment fragment, + View inflatedView, + ResolveStrategy resolveMembers) { ArgumentExceptionHelper.ThrowIfNull(fragment); ArgumentExceptionHelper.ThrowIfNull(inflatedView); @@ -232,6 +283,20 @@ public static void WireUpControls(this Fragment fragment, View inflatedView, Res } } + /// + /// Wires view controls to properties on an using the implicit resolve strategy. + /// + /// The activity whose properties should be wired. + /// Thrown when is null. + /// + /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. + /// + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] + public static void WireUpControls(this Activity activity) => + WireUpControls(activity, ResolveStrategy.Implicit); + /// /// Wires view controls to properties on an . /// @@ -241,9 +306,10 @@ public static void WireUpControls(this Fragment fragment, View inflatedView, Res /// /// Thrown when a property cannot be wired to a view with a corresponding resource identifier. /// - [RequiresUnreferencedCode("WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] + [RequiresUnreferencedCode( + "WireUpControls uses reflection to discover properties and attributes that may be trimmed.")] [RequiresDynamicCode("WireUpControls uses reflection that may require dynamic code generation.")] - public static void WireUpControls(this Activity activity, ResolveStrategy resolveMembers = ResolveStrategy.Implicit) + public static void WireUpControls(this Activity activity, ResolveStrategy resolveMembers) { ArgumentExceptionHelper.ThrowIfNull(activity); @@ -283,6 +349,10 @@ public static void WireUpControls(this Activity activity, ResolveStrategy resolv /// The collection is empty if no matching properties are found. [RequiresUnreferencedCode("Property discovery uses reflection and may require members removed by trimming.")] [RequiresDynamicCode("Property discovery uses reflection that may require dynamic code generation.")] + [SuppressMessage( + "Minor Code Smell", + "S4225:Extension methods should not extend object", + Justification = "Receiver is intentionally polymorphic across Android and AndroidX Fragment types.")] internal static PropertyInfo[] GetWireUpMembers(this object @this, ResolveStrategy resolveStrategy) { var type = @this.GetType(); @@ -303,48 +373,17 @@ internal static PropertyInfo[] GetWireUpMembersCached(Type type, ResolveStrategy { var members = key.Type.GetRuntimeProperties(); - // Materialize once into a list then to array; no LINQ in per-wire loops. - var list = new List(); + List list = []; foreach (var member in members) { - if (!member.CanWrite) - { - continue; - } - - switch (key.Strategy) + if (member.CanWrite && ShouldWireUpMember(member, key.Strategy)) { - case ResolveStrategy.ExplicitOptIn: - if (member.GetCustomAttribute(inherit: true) is not null) - { - list.Add(member); - } - - break; - - case ResolveStrategy.ExplicitOptOut: - if (typeof(View).IsAssignableFrom(member.PropertyType) && - member.GetCustomAttribute(inherit: true) is null) - { - list.Add(member); - } - - break; - - default: - // Implicit: either a View-typed property or explicitly marked with WireUpResource. - if (member.PropertyType.IsSubclassOf(typeof(View)) || - member.GetCustomAttribute(inherit: true) is not null) - { - list.Add(member); - } - - break; + list.Add(member); } } - return list.ToArray(); + return [.. list]; }); /// @@ -360,6 +399,27 @@ internal static string GetResourceName(this PropertyInfo member) return attr?.ResourceNameOverride ?? member.Name; } + /// + /// Determines whether a property should be wired up for the given resolve strategy. + /// + /// The candidate property. + /// The resolve strategy. + /// if the property should be wired up; otherwise . + [RequiresUnreferencedCode("Attribute lookup uses reflection and may require members removed by trimming.")] + [RequiresDynamicCode("Attribute lookup uses reflection that may require dynamic code generation.")] + private static bool ShouldWireUpMember(PropertyInfo member, ResolveStrategy strategy) => + strategy switch + { + ResolveStrategy.ExplicitOptIn => + member.GetCustomAttribute(true) is not null, + ResolveStrategy.ExplicitOptOut => + typeof(View).IsAssignableFrom(member.PropertyType) && + member.GetCustomAttribute(true) is null, + _ => + member.PropertyType.IsSubclassOf(typeof(View)) || + member.GetCustomAttribute(true) is not null, + }; + /// /// Gets a cached control for a root view and property name, fetching it if absent. /// @@ -384,7 +444,6 @@ internal static string GetResourceName(this PropertyInfo member) var created = fetchControlFromView(rootView, propertyName); - // ConcurrentDictionary indexer is safe; last write wins in a race. cache[propertyName] = created; return created; } @@ -427,7 +486,8 @@ internal static string GetResourceName(this PropertyInfo member) /// The resolved integer resource ID. /// Thrown when is null or empty. /// Thrown when the name cannot be resolved to an ID. - [RequiresUnreferencedCode("Android resource discovery uses reflection over generated resource types that may be trimmed.")] + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] [RequiresDynamicCode("Android resource discovery uses reflection that may require dynamic code generation.")] private static int GetControlIdByName(Assembly assembly, string name) { @@ -443,101 +503,126 @@ private static int GetControlIdByName(Assembly assembly, string name) return id; } - throw new MissingFieldException($"No Android resource id named '{name}' was found for assembly '{assembly.FullName}'."); + throw new MissingFieldException( + $"No Android resource id named '{name}' was found for {nameof(assembly)} '{assembly.FullName}'."); } /// - /// Builds an immutable mapping of resource name to integer ID for an assembly. + /// Builds a mapping of resource name to integer ID for an assembly. /// /// The assembly to inspect. /// A case-insensitive mapping of resource name to ID. - [RequiresUnreferencedCode("Android resource discovery uses reflection over generated resource types that may be trimmed.")] + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] [RequiresDynamicCode("Android resource discovery uses reflection that may require dynamic code generation.")] - private static IReadOnlyDictionary BuildIdMap(Assembly assembly) + private static Dictionary BuildIdMap(Assembly assembly) { -#if NET8_0_OR_GREATER - // Android .NET 8+ generates a resource designer in a referenced assembly. - var referenced = assembly.GetReferencedAssemblies(); - AssemblyName? designerName = null; + var resources = LocateResourceType(assembly); - for (var i = 0; i < referenced.Length; i++) - { - var an = referenced[i]; - if (an.FullName is not null && an.FullName.StartsWith("_Microsoft.Android.Resource.Designer", StringComparison.Ordinal)) - { - designerName = an; - break; - } - } + var idType = resources.GetNestedType("Id") + ?? throw new InvalidOperationException("Id is not a valid nested type in the generated resources."); - if (designerName is null) - { - throw new InvalidOperationException("Could not locate the Android resource designer assembly."); - } + return BuildIdMapFromFields(idType); + } + + /// + /// Locates the generated Android resource type for the specified assembly. + /// + /// The assembly to inspect. + /// The generated resource type. + /// Thrown when the resource type cannot be located. + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] + [RequiresDynamicCode("Android resource discovery uses reflection that may require dynamic code generation.")] + private static Type LocateResourceType(Assembly assembly) + { +#if NET8_0_OR_GREATER + var designerName = FindDesignerAssemblyName(assembly) + ?? throw new InvalidOperationException( + $"Could not locate the Android resource designer {nameof(assembly)}."); var resourcesAssembly = Assembly.Load(designerName); - var modules = resourcesAssembly.GetModules(); - Type? resources = null; - for (var i = 0; i < modules.Length && resources is null; i++) + return FindResourceType(resourcesAssembly.GetModules(), "ResourceConstant") + ?? throw new InvalidOperationException("Could not locate generated resource type 'ResourceConstant'."); +#else + return FindResourceType(assembly.GetModules(), "Resource") + ?? throw new InvalidOperationException("Could not locate generated resource type 'Resource'."); +#endif + } + +#if NET8_0_OR_GREATER + /// + /// Finds the name of the referenced Android resource designer assembly, if present. + /// + /// The assembly whose references should be inspected. + /// The designer assembly name, or if not found. + [RequiresUnreferencedCode("Inspects referenced assemblies via reflection.")] + private static AssemblyName? FindDesignerAssemblyName(Assembly assembly) + { + var referenced = assembly.GetReferencedAssemblies(); + + for (var i = 0; i < referenced.Length; i++) { - var types = modules[i].GetTypes(); - for (var j = 0; j < types.Length; j++) + var an = referenced[i]; + if (an.FullName?.StartsWith("_Microsoft.Android.Resource.Designer", StringComparison.Ordinal) == true) { - if (types[j].Name == "ResourceConstant") - { - resources = types[j]; - break; - } + return an; } } - if (resources is null) - { - throw new InvalidOperationException("Could not locate generated resource type 'ResourceConstant'."); - } -#else - var modules = assembly.GetModules(); - Type? resources = null; + return null; + } +#endif - for (var i = 0; i < modules.Length && resources is null; i++) + /// + /// Searches the supplied modules for a type with the given name. + /// + /// The modules to search. + /// The simple type name to match. + /// The matching type, or if not found. + [RequiresUnreferencedCode( + "Android resource discovery uses reflection over generated resource types that may be trimmed.")] + private static Type? FindResourceType(Module[] modules, string typeName) + { + for (var i = 0; i < modules.Length; i++) { var types = modules[i].GetTypes(); for (var j = 0; j < types.Length; j++) { - if (types[j].Name == "Resource") + if (types[j].Name == typeName) { - resources = types[j]; - break; + return types[j]; } } } - if (resources is null) - { - throw new InvalidOperationException("Could not locate generated resource type 'Resource'."); - } -#endif - - var idType = resources.GetNestedType("Id"); - if (idType is null) - { - throw new InvalidOperationException("Id is not a valid nested type in the generated resources."); - } + return null; + } + /// + /// Builds a case-insensitive name-to-id map from the integer constant fields of the supplied type. + /// + /// The nested Id type containing resource id constants. + /// A case-insensitive mapping of resource name to ID. + [SuppressMessage( + "Security Hotspot", + "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", + Justification = "Intentional reflection to wire up the view's own controls.")] + [UnconditionalSuppressMessage( + "Trimming", + "IL2070:UnrecognizedReflectionPattern", + Justification = "Reflects over the Android-preserved resource designer type; flow is already RequiresUnreferencedCode.")] + private static Dictionary BuildIdMapFromFields( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] Type idType) + { var fields = idType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - var dict = new Dictionary(fields.Length, StringComparer.InvariantCultureIgnoreCase); + Dictionary dict = new(fields.Length, StringComparer.InvariantCultureIgnoreCase); for (var i = 0; i < fields.Length; i++) { var f = fields[i]; - if (f.FieldType != typeof(int)) - { - continue; - } - - // Generated constants use raw constant values. - if (f.GetRawConstantValue() is int value) + if (f.FieldType == typeof(int) && f.GetRawConstantValue() is int value) { dict[f.Name] = value; } diff --git a/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs b/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs index b32ccb06db..1d1470adaf 100644 --- a/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs +++ b/src/ReactiveUI/Platforms/android/FlexibleCommandBinder.cs @@ -1,11 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Disposables; using System.Reflection; using System.Windows.Input; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -25,7 +28,14 @@ public abstract class FlexibleCommandBinder : ICreatesCommandBinding private readonly Dictionary _config = []; /// - public int GetAffinityForObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.PublicProperties)] T>(bool hasEventTarget) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public int GetAffinityForObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties)] + T>(bool hasEventTarget) { if (hasEventTarget) { @@ -33,9 +43,9 @@ public abstract class FlexibleCommandBinder : ICreatesCommandBinding } var match = _config.Keys - .Where(x => x.IsAssignableFrom(typeof(T))) - .OrderByDescending(x => _config[x].Affinity) - .FirstOrDefault(); + .Where(x => x.IsAssignableFrom(typeof(T))) + .OrderByDescending(x => _config[x].Affinity) + .FirstOrDefault(); if (match is null) { @@ -48,7 +58,11 @@ public abstract class FlexibleCommandBinder : ICreatesCommandBinding /// [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - public IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T>(ICommand? command, T? target, IObservable commandParameter) + public IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T>(ICommand? command, T? target, IObservable commandParameter) where T : class { ArgumentExceptionHelper.ThrowIfNull(target); @@ -56,22 +70,34 @@ public abstract class FlexibleCommandBinder : ICreatesCommandBinding var type = target.GetType(); var match = _config.Keys - .Where(x => x.IsAssignableFrom(type)) - .OrderByDescending(x => _config[x].Affinity) - .FirstOrDefault() ?? throw new NotSupportedException($"CommandBinding for {type.Name} is not supported"); + .Where(x => x.IsAssignableFrom(type)) + .OrderByDescending(x => _config[x].Affinity) + .FirstOrDefault() ?? throw new NotSupportedException($"CommandBinding for {type.Name} is not supported"); var typeProperties = _config[match]; - return typeProperties.CreateBinding?.Invoke(command, target, commandParameter) ?? Disposable.Empty; + return typeProperties.CreateBinding?.Invoke(command, target, commandParameter) ?? EmptyDisposable.Instance; } /// [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - public IDisposable? BindCommandToObject(ICommand? command, T? target, IObservable commandParameter, string eventName) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IDisposable? BindCommandToObject( + ICommand? command, + T? target, + IObservable commandParameter, + string eventName) where T : class - => Disposable.Empty; + => EmptyDisposable.Instance; /// - public IDisposable? BindCommandToObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] T, TEventArgs>( + public IDisposable? BindCommandToObject< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | + DynamicallyAccessedMemberTypes.PublicEvents | + DynamicallyAccessedMemberTypes.NonPublicEvents)] + T, TEventArgs>( ICommand? command, T? target, IObservable commandParameter, @@ -81,42 +107,24 @@ public abstract class FlexibleCommandBinder : ICreatesCommandBinding where TEventArgs : EventArgs { ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(addHandler); - ArgumentNullException.ThrowIfNull(removeHandler); + ArgumentExceptionHelper.ThrowIfNull(addHandler); + ArgumentExceptionHelper.ThrowIfNull(removeHandler); - // Match existing binder behavior: if there is no command, create a no-op binding. if (command is null) { - return Disposable.Empty; + return EmptyDisposable.Instance; } - // Keep the existing "null means use target" idiom used by ForEvent. - commandParameter ??= Observable.Return((object?)target); + commandParameter ??= new SingleValueObservable(target); - // The latest parameter may be updated from a different thread than the event thread. object? latestParam = null; - // Stable handler for deterministic unsubscription. - void Handler(object? sender, TEventArgs e) - { - var param = Volatile.Read(ref latestParam); - if (command.CanExecute(param)) - { - command.Execute(param); - } - } - - // Subscribe to parameter updates first, then attach the event handler. - var parameterSub = commandParameter.Subscribe(x => Volatile.Write(ref latestParam, x)); + var parameterSub = commandParameter.Subscribe(new DelegateObserver(x => Volatile.Write(ref latestParam, x))); addHandler(Handler); - // If we can locate a conventional enabled property on the runtime target, keep it in sync with CanExecute. - // This is intentionally best-effort and does not throw if the property is absent or cannot be set. Action? enabledSetter = null; try { - // Common Android idiom: "Enabled" boolean property. - // Use runtime type so derived types are supported. var enabledProp = typeof(T).GetRuntimeProperty("Enabled"); if (enabledProp is not null) { @@ -125,39 +133,37 @@ void Handler(object? sender, TEventArgs e) } catch { - // Best-effort only; ignore reflection failures. enabledSetter = null; } IDisposable? canExecuteSub = null; if (enabledSetter is not null) { - // Initial enabled state (default parameter is null until the first commandParameter emission). enabledSetter(target, command.CanExecute(Volatile.Read(ref latestParam)), null); - // Keep Enabled in sync with CanExecuteChanged. - canExecuteSub = Observable.FromEvent( - eventHandler => - { - void CanExecuteHandler(object? s, EventArgs e) => - eventHandler(command.CanExecute(Volatile.Read(ref latestParam))); - return CanExecuteHandler; - }, - h => command.CanExecuteChanged += h, - h => command.CanExecuteChanged -= h) - .Subscribe(x => enabledSetter(target, x, null)); + canExecuteSub = new CanExecuteChangedObservable(command, () => command.CanExecute(Volatile.Read(ref latestParam))) + .Subscribe(new DelegateObserver(x => enabledSetter(target, x, null))); } - // Dispose ordering: detach event handler and CanExecute subscription after stopping parameter updates. - // The handler instance is stable, so Remove is correct. return canExecuteSub is null ? new CompositeDisposable( parameterSub, - Disposable.Create(() => removeHandler(Handler))) + new ActionDisposable(() => removeHandler(Handler))) : new CompositeDisposable( parameterSub, canExecuteSub, - Disposable.Create(() => removeHandler(Handler))); + new ActionDisposable(() => removeHandler(Handler))); + + void Handler(object? sender, TEventArgs e) + { + var param = Volatile.Read(ref latestParam); + if (!command.CanExecute(param)) + { + return; + } + + command.Execute(param); + } } /// @@ -170,22 +176,29 @@ void CanExecuteHandler(object? s, EventArgs e) => /// Event name. /// Enabled property name. [RequiresUnreferencedCode("String/reflection-based event binding may require members removed by trimming.")] - protected static IDisposable ForEvent(ICommand? command, object? target, IObservable commandParameter, string eventName, PropertyInfo enabledProperty) + protected static IDisposable ForEvent( + ICommand? command, + object? target, + IObservable commandParameter, + string eventName, + PropertyInfo enabledProperty) { ArgumentExceptionHelper.ThrowIfNull(command); - commandParameter ??= Observable.Return(target); + commandParameter ??= new SingleValueObservable(target); object? latestParam = null; var ctl = target!; - var actionDisp = Observable.FromEventPattern(ctl, eventName).Subscribe(_ => + var actionDisp = new EventPatternObservable(ctl, eventName).Subscribe(new DelegateObserver(_ => { - if (command.CanExecute(latestParam)) + if (!command.CanExecute(latestParam)) { - command.Execute(latestParam); + return; } - }); + + command.Execute(latestParam); + })); var enabledSetter = Reflection.GetValueSetterForProperty(enabledProperty); if (enabledSetter is null) @@ -193,21 +206,13 @@ protected static IDisposable ForEvent(ICommand? command, object? target, IObserv return actionDisp; } - // initial enabled state enabledSetter(target, command.CanExecute(latestParam), null); return new CompositeDisposable( - actionDisp, - commandParameter.Subscribe(x => latestParam = x), - Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(command.CanExecute(latestParam)); - return Handler; - }, - x => command.CanExecuteChanged += x, - x => command.CanExecuteChanged -= x) - .Subscribe(x => enabledSetter(target, x, null))); + actionDisp, + commandParameter.Subscribe(new DelegateObserver(x => latestParam = x)), + new CanExecuteChangedObservable(command, () => command.CanExecute(latestParam)) + .Subscribe(new DelegateObserver(x => enabledSetter(target, x, null)))); } /// @@ -229,67 +234,54 @@ protected static IDisposable ForEvent(ICommand? command, object? target, IObserv /// Thrown when is . /// protected static IDisposable ForEvent( - ICommand? command, - object? target, - IObservable? commandParameter, - Action> addHandler, - Action> removeHandler, - Action? enabledSetter) + ICommand? command, + object? target, + IObservable? commandParameter, + Action> addHandler, + Action> removeHandler, + Action? enabledSetter) where TEventArgs : EventArgs { ArgumentExceptionHelper.ThrowIfNull(command); ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(addHandler); - ArgumentNullException.ThrowIfNull(removeHandler); + ArgumentExceptionHelper.ThrowIfNull(addHandler); + ArgumentExceptionHelper.ThrowIfNull(removeHandler); - // Preserve existing idiom: null commandParameter means use target. - commandParameter ??= Observable.Return(target); + commandParameter ??= new SingleValueObservable(target); object? latestParam = null; - // Stable handler for deterministic unsubscription. - void Handler(object? sender, TEventArgs e) - { - var param = Volatile.Read(ref latestParam); - if (command.CanExecute(param)) - { - command.Execute(param); - } - } + var parameterSub = commandParameter.Subscribe(new DelegateObserver(x => Volatile.Write(ref latestParam, x))); - // Subscribe to parameter updates first so the first event sees the latest parameter. - var parameterSub = commandParameter.Subscribe(x => Volatile.Write(ref latestParam, x)); - - // Hook the event without reflection. addHandler(Handler); - // If there is no enabled setter, we're done. if (enabledSetter is null) { return new CompositeDisposable( parameterSub, - Disposable.Create(() => removeHandler(Handler))); + new ActionDisposable(() => removeHandler(Handler))); } - // Initial enabled state. enabledSetter(target, command.CanExecute(Volatile.Read(ref latestParam)), null); - // Keep enabled state in sync with CanExecuteChanged. - var canExecuteSub = Observable.FromEvent( - eventHandler => - { - void CanExecuteHandler(object? s, EventArgs e) => - eventHandler(command.CanExecute(Volatile.Read(ref latestParam))); - return CanExecuteHandler; - }, - h => command.CanExecuteChanged += h, - h => command.CanExecuteChanged -= h) - .Subscribe(x => enabledSetter(target, x, null)); + var canExecuteSub = new CanExecuteChangedObservable(command, () => command.CanExecute(Volatile.Read(ref latestParam))) + .Subscribe(new DelegateObserver(x => enabledSetter(target, x, null))); return new CompositeDisposable( parameterSub, canExecuteSub, - Disposable.Create(() => removeHandler(Handler))); + new ActionDisposable(() => removeHandler(Handler))); + + void Handler(object? sender, TEventArgs e) + { + var param = Volatile.Read(ref latestParam); + if (!command.CanExecute(param)) + { + return; + } + + command.Execute(param); + } } /// @@ -310,66 +302,53 @@ void CanExecuteHandler(object? s, EventArgs e) => /// Thrown when is . /// protected static IDisposable ForEvent( - ICommand? command, - object? target, - IObservable? commandParameter, - Action addHandler, - Action removeHandler, - Action? enabledSetter) + ICommand? command, + object? target, + IObservable? commandParameter, + Action addHandler, + Action removeHandler, + Action? enabledSetter) { ArgumentExceptionHelper.ThrowIfNull(command); ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(addHandler); - ArgumentNullException.ThrowIfNull(removeHandler); + ArgumentExceptionHelper.ThrowIfNull(addHandler); + ArgumentExceptionHelper.ThrowIfNull(removeHandler); - // Preserve existing idiom: null commandParameter means use target. - commandParameter ??= Observable.Return(target); + commandParameter ??= new SingleValueObservable(target); object? latestParam = null; - // Stable handler for deterministic unsubscription. - void Handler(object? sender, EventArgs e) - { - var param = Volatile.Read(ref latestParam); - if (command.CanExecute(param)) - { - command.Execute(param); - } - } + var parameterSub = commandParameter.Subscribe(new DelegateObserver(x => Volatile.Write(ref latestParam, x))); - // Subscribe to parameter updates first so the first event sees the latest parameter. - var parameterSub = commandParameter.Subscribe(x => Volatile.Write(ref latestParam, x)); - - // Hook the event without reflection. addHandler(Handler); - // If there is no enabled setter, we're done. if (enabledSetter is null) { return new CompositeDisposable( parameterSub, - Disposable.Create(() => removeHandler(Handler))); + new ActionDisposable(() => removeHandler(Handler))); } - // Initial enabled state. enabledSetter(target, command.CanExecute(Volatile.Read(ref latestParam)), null); - // Keep enabled state in sync with CanExecuteChanged. - var canExecuteSub = Observable.FromEvent( - eventHandler => - { - void CanExecuteHandler(object? s, EventArgs e) => - eventHandler(command.CanExecute(Volatile.Read(ref latestParam))); - return CanExecuteHandler; - }, - h => command.CanExecuteChanged += h, - h => command.CanExecuteChanged -= h) - .Subscribe(x => enabledSetter(target, x, null)); + var canExecuteSub = new CanExecuteChangedObservable(command, () => command.CanExecute(Volatile.Read(ref latestParam))) + .Subscribe(new DelegateObserver(x => enabledSetter(target, x, null))); return new CompositeDisposable( parameterSub, canExecuteSub, - Disposable.Create(() => removeHandler(Handler))); + new ActionDisposable(() => removeHandler(Handler))); + + void Handler(object? sender, EventArgs e) + { + var param = Volatile.Read(ref latestParam); + if (!command.CanExecute(param)) + { + return; + } + + command.Execute(param); + } } /// @@ -378,15 +357,45 @@ void CanExecuteHandler(object? s, EventArgs e) => /// Type. /// The affinity for the type. /// Creates the binding. - protected void Register(Type type, int affinity, Func, IDisposable> createBinding) => _config[type] = new CommandBindingInfo { Affinity = affinity, CreateBinding = createBinding }; + protected void Register( + Type type, + int affinity, + Func, IDisposable> createBinding) => + _config[type] = new() { Affinity = affinity, CreateBinding = createBinding }; + + /// + /// Emits the command's current can-execute value each time fires — + /// replacing Observable.FromEvent over that event. The handler is detached when the subscription is disposed. + /// + /// The command whose event is observed. + /// Reads the command's current can-execute value when the event fires. + private sealed class CanExecuteChangedObservable(ICommand command, Func readCanExecute) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + void Handler(object? sender, EventArgs e) => observer.OnNext(readCanExecute()); + + command.CanExecuteChanged += Handler; + return new ActionDisposable(() => command.CanExecuteChanged -= Handler); + } + } /// /// Provides information about a command binding, including its affinity and a factory for creating the binding. /// - private class CommandBindingInfo + private sealed class CommandBindingInfo { + /// + /// Gets or sets the affinity that ranks this binding against others for the same type. + /// public int Affinity { get; set; } + /// + /// Gets or sets the factory that creates the command binding. + /// public Func, IDisposable>? CreateBinding { get; set; } } } diff --git a/src/ReactiveUI/Platforms/android/HandlerScheduler.cs b/src/ReactiveUI/Platforms/android/HandlerScheduler.cs index 9c919f2ae7..4a1d9b3100 100644 --- a/src/ReactiveUI/Platforms/android/HandlerScheduler.cs +++ b/src/ReactiveUI/Platforms/android/HandlerScheduler.cs @@ -1,10 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; +using System.Reactive.Disposables; using Android.OS; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -18,8 +22,26 @@ namespace ReactiveUI; /// The handler. public class HandlerScheduler(Handler handler) : IScheduler, IEnableLogger { + /// + /// The number of 100-nanosecond ticks per microsecond, used to convert + /// a tick count into microseconds. + /// + private const long TicksPerMicrosecond = 10; + + /// + /// The number of microseconds per millisecond, used to convert microseconds + /// into the millisecond delay expected by . + /// + private const long MicrosecondsPerMillisecond = 1000; + + /// + /// The Android handler used to post work to the target thread. + /// private readonly Handler _handler = handler; + /// + /// Initializes static members of the class. + /// static HandlerScheduler() => MainThreadScheduler = new HandlerScheduler(new(Looper.MainLooper!)); @@ -29,13 +51,17 @@ static HandlerScheduler() => public static IScheduler MainThreadScheduler { get; } /// + [SuppressMessage( + "Major Code Smell", + "S6354:Use a testable (date) time provider", + Justification = "Scheduler intentionally uses real time.")] public DateTimeOffset Now => DateTimeOffset.Now; /// public IDisposable Schedule(TState state, Func action) { var isCancelled = false; - var innerDisp = new SerialDisposable() { Disposable = Disposable.Empty }; + SwapDisposable innerDisp = new() { Disposable = EmptyDisposable.Instance }; _handler.Post(() => { @@ -48,35 +74,42 @@ public IDisposable Schedule(TState state, Func isCancelled = true), - innerDisp); + new ActionDisposable(() => isCancelled = true), + innerDisp); } /// - public IDisposable Schedule(TState state, TimeSpan dueTime, Func action) // TODO: Create Test + public IDisposable + Schedule( + TState state, + TimeSpan dueTime, + Func action) { var isCancelled = false; - var innerDisp = new SerialDisposable() { Disposable = Disposable.Empty }; + SwapDisposable innerDisp = new() { Disposable = EmptyDisposable.Instance }; _handler.PostDelayed( - () => - { - if (isCancelled) - { - return; - } + () => + { + if (isCancelled) + { + return; + } - innerDisp.Disposable = action(this, state); - }, - dueTime.Ticks / 10 / 1000); + innerDisp.Disposable = action(this, state); + }, + dueTime.Ticks / TicksPerMicrosecond / MicrosecondsPerMillisecond); return new CompositeDisposable( - Disposable.Create(() => isCancelled = true), - innerDisp); + new ActionDisposable(() => isCancelled = true), + innerDisp); } /// - public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) // TODO: Create Test + public IDisposable Schedule( + TState state, + DateTimeOffset dueTime, + Func action) { if (dueTime <= Now) { diff --git a/src/ReactiveUI/Platforms/android/ILayoutViewHost.cs b/src/ReactiveUI/Platforms/android/ILayoutViewHost.cs index 6683cce83e..8c74860513 100644 --- a/src/ReactiveUI/Platforms/android/ILayoutViewHost.cs +++ b/src/ReactiveUI/Platforms/android/ILayoutViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/android/IgnoreResourceAttribute.cs b/src/ReactiveUI/Platforms/android/IgnoreResourceAttribute.cs index 2dd581696b..e50ba3c3db 100644 --- a/src/ReactiveUI/Platforms/android/IgnoreResourceAttribute.cs +++ b/src/ReactiveUI/Platforms/android/IgnoreResourceAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/android/JavaHolder.cs b/src/ReactiveUI/Platforms/android/JavaHolder.cs index b77cad40bb..5a08692df8 100644 --- a/src/ReactiveUI/Platforms/android/JavaHolder.cs +++ b/src/ReactiveUI/Platforms/android/JavaHolder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,6 +15,12 @@ namespace ReactiveUI; /// The Java object instance to be held. Cannot be null. internal class JavaHolder(object instance) : Object { - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401: Field should be private", Justification = "Used for interop purposes")] + /// + /// The held Java object instance used for interop scenarios. + /// + [SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1401: Field should be private", + Justification = "Used for interop purposes")] public readonly object Instance = instance; } diff --git a/src/ReactiveUI/Platforms/android/LayoutViewHost.cs b/src/ReactiveUI/Platforms/android/LayoutViewHost.cs index e69151f1a6..9f9aabbfea 100644 --- a/src/ReactiveUI/Platforms/android/LayoutViewHost.cs +++ b/src/ReactiveUI/Platforms/android/LayoutViewHost.cs @@ -1,11 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.Content; using Android.Views; - using static ReactiveUI.ControlFetcherMixin; namespace ReactiveUI; @@ -23,6 +22,9 @@ namespace ReactiveUI; /// public abstract class LayoutViewHost : ILayoutViewHost, IEnableLogger { + /// + /// The backing view instance owned by this host. + /// private View? _view; /// @@ -36,7 +38,7 @@ protected LayoutViewHost() { } - /// + /// /// Initializes a new instance of the class by inflating /// a layout resource. /// @@ -59,8 +61,8 @@ protected LayoutViewHost( ViewGroup parent, bool attachToRoot) { - ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(parent); + ArgumentExceptionHelper.ThrowIfNull(context); + ArgumentExceptionHelper.ThrowIfNull(parent); View = Inflate(context, layoutId, parent, attachToRoot); } @@ -92,12 +94,14 @@ protected LayoutViewHost( Action bind) : this(context, layoutId, parent, attachToRoot) { - ArgumentNullException.ThrowIfNull(bind); + ArgumentExceptionHelper.ThrowIfNull(bind); - if (View is not null) + if (View is null) { - bind(this, View); + return; } + + bind(this, View); } /// @@ -132,15 +136,17 @@ protected LayoutViewHost( bool performAutoWireup, ResolveStrategy resolveStrategy) { - ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(parent); + ArgumentExceptionHelper.ThrowIfNull(context); + ArgumentExceptionHelper.ThrowIfNull(parent); View = Inflate(context, layoutId, parent, attachToRoot); - if (performAutoWireup) + if (!performAutoWireup) { - this.WireUpControls(resolveStrategy); + return; } + + this.WireUpControls(resolveStrategy); } /// @@ -156,7 +162,6 @@ public View? View _view = value; - // Associate the host with the view for retrieval via ViewMixins. _view?.SetTag(ViewMixins.ViewHostTag, this.ToJavaObject()); } } @@ -171,6 +176,12 @@ public View? View return host._view; } + /// + /// Gets the backing for this host as a friendly alternate to the implicit operator. + /// + /// The backing instance, or if none has been assigned. + public View? ToView() => _view; + /// /// Inflates an Android layout resource into a using the provided context. /// diff --git a/src/ReactiveUI/Platforms/android/LinkerOverrides.cs b/src/ReactiveUI/Platforms/android/LinkerOverrides.cs index 278cbecb38..dcda3aab8d 100644 --- a/src/ReactiveUI/Platforms/android/LinkerOverrides.cs +++ b/src/ReactiveUI/Platforms/android/LinkerOverrides.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Runtime.Versioning; - using Android.Widget; namespace ReactiveUI; @@ -17,37 +16,43 @@ namespace ReactiveUI; [Preserve(AllMembers = true)] internal class LinkerOverrides { + /// + /// Preserves the following Android UI types and their members. + /// [ObsoletedOSPlatform("android30.0")] [SupportedOSPlatform("android23.0")] -#pragma warning disable CA1822 // Mark members as static -#pragma warning disable IDE0051 // Remove unused private members + [SuppressMessage("ReSharper", "UnusedMember.Local", Justification = "Used by linker.")] + [SuppressMessage("Major Bug", "S1656:Variables should not be self-assigned", Justification = "Used by linker.")] + [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Used by linker.")] + [SuppressMessage( + "Style", + "IDE0051:Private member is unused", + Justification = "Linker preservation member.")] private void KeepMe() -#pragma warning restore IDE0051 // Remove unused private members -#pragma warning restore CA1822 // Mark members as static { - var txt = new TextView(null); + TextView txt = new(null); txt.Text = txt.Text; - var iv = new ImageView(null); + ImageView iv = new(null); _ = iv.Drawable; - var prog = new ProgressBar(null); + ProgressBar prog = new(null); prog.Progress = prog.Progress; - var cb = new RadioButton(null); + RadioButton cb = new(null); cb.Checked = cb.Checked; - var np = new NumberPicker(null); + NumberPicker np = new(null); np.Value = np.Value; - var rb = new RatingBar(null); + RatingBar rb = new(null); rb.Rating = rb.Rating; - var cv = new CalendarView(null!); + CalendarView cv = new(null!); cv.Date = cv.Date; - var th = new TabHost(null); + TabHost th = new(null); th.CurrentTab = th.CurrentTab; - var tp = new TimePicker(null); + TimePicker tp = new(null); tp.Hour = tp.Hour; tp.Minute = tp.Minute; } diff --git a/src/ReactiveUI/Platforms/android/ObjectExtension.cs b/src/ReactiveUI/Platforms/android/ObjectExtension.cs index 6243cf0b6f..b638b9ca4b 100644 --- a/src/ReactiveUI/Platforms/android/ObjectExtension.cs +++ b/src/ReactiveUI/Platforms/android/ObjectExtension.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -27,6 +27,10 @@ internal static class ObjectExtension /// The .NET object of type TObject represented by the specified Java.Lang.Object, or the default value of TObject /// if value is null. /// Thrown if value is not a Java.Lang.Object created with .ToJavaObject(). + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static TObject ToNetObject(this Object value) { if (value is null) @@ -36,7 +40,8 @@ public static TObject ToNetObject(this Object value) if (value is not JavaHolder) { - throw new InvalidOperationException("Unable to convert to .NET object. Only Java.Lang.Object created with .ToJavaObject() can be converted."); + throw new InvalidOperationException( + "Unable to convert to .NET object. Only Java.Lang.Object created with .ToJavaObject() can be converted."); } return (TObject)((JavaHolder)value).Instance; diff --git a/src/ReactiveUI/Platforms/android/PlatformOperations.cs b/src/ReactiveUI/Platforms/android/PlatformOperations.cs index ad9979973a..3c3628d117 100644 --- a/src/ReactiveUI/Platforms/android/PlatformOperations.cs +++ b/src/ReactiveUI/Platforms/android/PlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,7 +15,7 @@ namespace ReactiveUI; public class PlatformOperations : IPlatformOperations { /// - public string? GetOrientation() // TODO: Create Test + public string? GetOrientation() { if (Application.Context.GetSystemService(Context.WindowService) is not IWindowManager wm) { diff --git a/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs index 8059b624c0..c670363613 100644 --- a/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/android/PlatformRegistrations.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; + namespace ReactiveUI; /// @@ -12,7 +14,7 @@ namespace ReactiveUI; public class PlatformRegistrations : IWantsToRegisterStuff { /// - public void Register(IRegistrar registrar) // TODO: Create Test + public void Register(IRegistrar registrar) { ArgumentExceptionHelper.ThrowIfNull(registrar); diff --git a/src/ReactiveUI/Platforms/android/ReactiveActivity.cs b/src/ReactiveUI/Platforms/android/ReactiveActivity.cs index 8cead871e4..119216b003 100644 --- a/src/ReactiveUI/Platforms/android/ReactiveActivity.cs +++ b/src/ReactiveUI/Platforms/android/ReactiveActivity.cs @@ -1,23 +1,39 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; +using System.Reactive; using Android.App; using Android.Content; using Android.Runtime; +using ReactiveUI.Internal; + namespace ReactiveUI; /// /// This is an Activity that is both an Activity and has ReactiveObject powers /// (i.e. you can call RaiseAndSetIfChanged). /// -public class ReactiveActivity : Activity, IReactiveObject, IReactiveNotifyPropertyChanged, IHandleObservableErrors +public class ReactiveActivity : Activity, IReactiveObject, IReactiveNotifyPropertyChanged, + IHandleObservableErrors { - private readonly Subject _activated = new(); - private readonly Subject _deactivated = new(); - private readonly Subject<(int requestCode, Result resultCode, Intent? intent)> _activityResult = new(); + /// + /// The subject that signals when the activity is activated. + /// + private readonly BroadcastSubject _activated = new(); + + /// + /// The subject that signals when the activity is deactivated. + /// + private readonly BroadcastSubject _deactivated = new(); + + /// + /// The subject that signals activity results. + /// + private readonly BroadcastSubject<(int requestCode, Result resultCode, Intent? intent)> _activityResult = new(); /// /// Initializes a new instance of the class. @@ -54,12 +70,12 @@ protected ReactiveActivity(in IntPtr handle, JniHandleOwnership ownership) /// /// Gets a signal when the activity is activated. /// - public IObservable Activated => _activated.AsObservable(); // TODO: Create Test + public IObservable Activated => _activated; /// /// Gets a signal when the activity is deactivated. /// - public IObservable Deactivated => _deactivated.AsObservable(); // TODO: Create Test + public IObservable Deactivated => _deactivated; /// /// Gets the activity result. @@ -67,8 +83,7 @@ protected ReactiveActivity(in IntPtr handle, JniHandleOwnership ownership) /// /// The activity result. /// - public IObservable<(int requestCode, Result resultCode, Intent? intent)> ActivityResult => // TODO: Create Test - _activityResult.AsObservable(); + public IObservable<(int requestCode, Result resultCode, Intent? intent)> ActivityResult => _activityResult; /// /// When this method is called, an object will not fire change @@ -77,16 +92,13 @@ protected ReactiveActivity(in IntPtr handle, JniHandleOwnership ownership) /// /// An object that, when disposed, reenables change /// notifications. - public IDisposable SuppressChangeNotifications() => // TODO: Create Test - IReactiveObjectExtensions.SuppressChangeNotifications(this); + public IDisposable SuppressChangeNotifications() => IReactiveObjectExtensions.SuppressChangeNotifications(this); /// - void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => // TODO: Create Test - PropertyChanging?.Invoke(this, args); + void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => PropertyChanging?.Invoke(this, args); /// - void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => // TODO: Create Test - PropertyChanged?.Invoke(this, args); + void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args); /// /// Starts the activity for result asynchronously. @@ -94,15 +106,10 @@ void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => // T /// The intent. /// The request code. /// A task with the result and the intent. - public Task<(Result resultCode, Intent? intent)> StartActivityForResultAsync(Intent? intent, int requestCode) // TODO: Create Test + public Task<(Result resultCode, Intent? intent)> + StartActivityForResultAsync(Intent? intent, int requestCode) { - // NB: It's important that we set up the subscription *before* we - // call ActivityForResult - var ret = ActivityResult - .Where(x => x.requestCode == requestCode) - .Select(x => (x.resultCode, x.intent)) - .FirstAsync() - .ToTask(); + var ret = ActivityResultAwaiter.Await(ActivityResult, requestCode); StartActivityForResult(intent, requestCode); return ret; @@ -114,15 +121,10 @@ void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => // T /// The type. /// The request code. /// A task with the result and intent. - public Task<(Result resultCode, Intent? intent)> StartActivityForResultAsync(Type type, int requestCode) // TODO: Create Test + public Task<(Result resultCode, Intent? intent)> + StartActivityForResultAsync(Type type, int requestCode) { - // NB: It's important that we set up the subscription *before* we - // call ActivityForResult - var ret = ActivityResult - .Where(x => x.requestCode == requestCode) - .Select(x => (x.resultCode, x.intent)) - .FirstAsync() - .ToTask(); + var ret = ActivityResultAwaiter.Await(ActivityResult, requestCode); StartActivityForResult(type, requestCode); return ret; @@ -161,4 +163,80 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + + /// + /// Completes a task with the first activity result matching a request code, then unsubscribes — replacing + /// ActivityResult.Where(matching).Select(...).FirstAsync().ToTask(). + /// + private sealed class ActivityResultAwaiter + : IObserver<(int requestCode, Result resultCode, Intent? intent)>, IDisposable + { + /// The request code this awaiter is waiting for. + private readonly int _requestCode; + + /// Completes when the first matching activity result arrives. + private readonly TaskCompletionSource<(Result resultCode, Intent? intent)> _completion = new(); + + /// The subscription to the activity-result stream. + private readonly OnceDisposable _subscription = new(); + + /// Set to 1 once the task has been settled, so only the first matching result wins. + private int _settled; + + /// Initializes a new instance of the class. + /// The request code to wait for. + private ActivityResultAwaiter(int requestCode) => _requestCode = requestCode; + + /// Subscribes to and returns a task that completes on the first matching result. + /// The activity-result stream. + /// The request code to wait for. + /// A task carrying the matching result and intent. + public static Task<(Result resultCode, Intent? intent)> Await( + IObservable<(int requestCode, Result resultCode, Intent? intent)> source, + int requestCode) + { + var awaiter = new ActivityResultAwaiter(requestCode); + awaiter._subscription.Disposable = source.Subscribe(awaiter); + return awaiter._completion.Task; + } + + /// + public void OnNext((int requestCode, Result resultCode, Intent? intent) value) + { + if (value.requestCode != _requestCode || Interlocked.Exchange(ref _settled, 1) != 0) + { + return; + } + + _completion.TrySetResult((value.resultCode, value.intent)); + Dispose(); + } + + /// + public void OnError(Exception error) + { + if (Interlocked.Exchange(ref _settled, 1) != 0) + { + return; + } + + _completion.TrySetException(error); + Dispose(); + } + + /// + public void OnCompleted() + { + if (Interlocked.Exchange(ref _settled, 1) != 0) + { + return; + } + + _completion.TrySetCanceled(); + Dispose(); + } + + /// + public void Dispose() => _subscription.Dispose(); + } } diff --git a/src/ReactiveUI/Platforms/android/ReactiveActivity{TViewModel}.cs b/src/ReactiveUI/Platforms/android/ReactiveActivity{TViewModel}.cs index 736d51705b..195cb1befd 100644 --- a/src/ReactiveUI/Platforms/android/ReactiveActivity{TViewModel}.cs +++ b/src/ReactiveUI/Platforms/android/ReactiveActivity{TViewModel}.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,6 +15,9 @@ namespace ReactiveUI; public class ReactiveActivity : ReactiveActivity, IViewFor, ICanActivate where TViewModel : class { + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// diff --git a/src/ReactiveUI/Platforms/android/ReactiveFragment.cs b/src/ReactiveUI/Platforms/android/ReactiveFragment.cs index 22762289c6..30d508bbb0 100644 --- a/src/ReactiveUI/Platforms/android/ReactiveFragment.cs +++ b/src/ReactiveUI/Platforms/android/ReactiveFragment.cs @@ -1,23 +1,34 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; +using System.Reactive; using System.Runtime.Versioning; - using Android.App; using Android.Runtime; +using ReactiveUI.Internal; + namespace ReactiveUI; /// /// This is a Fragment that is both an Activity and has ReactiveObject powers /// (i.e. you can call RaiseAndSetIfChanged). /// -public class ReactiveFragment : Fragment, IReactiveNotifyPropertyChanged, IReactiveObject, IHandleObservableErrors +public class ReactiveFragment : Fragment, IReactiveNotifyPropertyChanged, IReactiveObject, + IHandleObservableErrors { - private readonly Subject _activated = new(); - private readonly Subject _deactivated = new(); + /// + /// The subject that signals when the fragment is activated. + /// + private readonly BroadcastSubject _activated = new(); + + /// + /// The subject that signals when the fragment is deactivated. + /// + private readonly BroadcastSubject _deactivated = new(); /// /// Initializes a new instance of the class. @@ -58,7 +69,7 @@ protected ReactiveFragment(in IntPtr handle, JniHandleOwnership ownership) /// /// The activated. /// - public IObservable Activated => _activated.AsObservable(); + public IObservable Activated => _activated; /// /// Gets a signal when the fragment is deactivated. @@ -66,7 +77,7 @@ protected ReactiveFragment(in IntPtr handle, JniHandleOwnership ownership) /// /// The deactivated. /// - public IObservable Deactivated => _deactivated.AsObservable(); + public IObservable Deactivated => _deactivated; /// void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => PropertyChanging?.Invoke(this, args); diff --git a/src/ReactiveUI/Platforms/android/ReactiveFragment{TViewModel}.cs b/src/ReactiveUI/Platforms/android/ReactiveFragment{TViewModel}.cs index a6bed33e27..44bec17b09 100644 --- a/src/ReactiveUI/Platforms/android/ReactiveFragment{TViewModel}.cs +++ b/src/ReactiveUI/Platforms/android/ReactiveFragment{TViewModel}.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Runtime.Versioning; - using Android.Runtime; namespace ReactiveUI; @@ -17,6 +16,9 @@ namespace ReactiveUI; public class ReactiveFragment : ReactiveFragment, IViewFor, ICanActivate where TViewModel : class { + /// + /// The backing field for the view model. + /// private TViewModel? _viewModel; /// diff --git a/src/ReactiveUI/Platforms/android/ReactiveViewHost.cs b/src/ReactiveUI/Platforms/android/ReactiveViewHost.cs index bf38052057..182bda9846 100644 --- a/src/ReactiveUI/Platforms/android/ReactiveViewHost.cs +++ b/src/ReactiveUI/Platforms/android/ReactiveViewHost.cs @@ -1,15 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; using System.Reflection; -using System.Runtime.Serialization; - using Android.Content; using Android.Views; - using static ReactiveUI.ControlFetcherMixin; namespace ReactiveUI; @@ -43,7 +40,10 @@ public abstract class ReactiveViewHost : /// This field is used by legacy reflection-based wiring. It is not initialized by default in AOT-safe construction /// paths to avoid reflection and allocations. If a derived type requires this, use the legacy constructor. /// - [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401: Field should be private", Justification = "Legacy reasons")] + [SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1401: Field should be private", + Justification = "Legacy reasons")] [SuppressMessage("Design", "CA1051: Do not declare visible instance fields", Justification = "Legacy reasons")] [IgnoreDataMember] [JsonIgnore] @@ -61,9 +61,21 @@ public abstract class ReactiveViewHost : /// This constructor performs no inflation or wiring and is AOT-safe. /// Derived types may assign manually. /// - protected ReactiveViewHost() + protected ReactiveViewHost() => SetupRxObjAot(); + + /// + /// Initializes a new instance of the class by inflating a layout resource. + /// + /// The Android context. + /// The layout resource identifier. + /// The parent view group. + /// + /// This constructor is fully AOT- and trimming-safe and performs no reflection-based auto-wireup. + /// The inflated view is not attached to the parent. + /// + protected ReactiveViewHost(Context ctx, int layoutId, ViewGroup parent) + : this(ctx, layoutId, parent, false) { - SetupRxObjAot(); } /// @@ -76,11 +88,9 @@ protected ReactiveViewHost() /// /// This constructor is fully AOT- and trimming-safe and performs no reflection-based auto-wireup. /// - protected ReactiveViewHost(Context ctx, int layoutId, ViewGroup parent, bool attachToRoot = false) - : base(ctx, layoutId, parent, attachToRoot) - { + protected ReactiveViewHost(Context ctx, int layoutId, ViewGroup parent, bool attachToRoot) + : base(ctx, layoutId, parent, attachToRoot) => SetupRxObjAot(); - } /// /// Initializes a new instance of the class by inflating a layout resource @@ -97,19 +107,20 @@ protected ReactiveViewHost(Context ctx, int layoutId, ViewGroup parent, bool att /// This constructor is fully AOT-safe and avoids reflection entirely. /// /// Thrown when is . - protected ReactiveViewHost(Context ctx, int layoutId, ViewGroup parent, bool attachToRoot, Action, View> bind) + protected ReactiveViewHost( + Context ctx, + int layoutId, + ViewGroup parent, + bool attachToRoot, + Action, View> bind) : base( ctx, layoutId, parent, attachToRoot, - (host, view) => - { - // The base constructor guarantees 'host' is the derived instance. - bind((ReactiveViewHost)host, view); - }) + (host, view) => bind((ReactiveViewHost)host, view)) { - ArgumentNullException.ThrowIfNull(bind); + ArgumentExceptionHelper.ThrowIfNull(bind); SetupRxObjAot(); } @@ -140,10 +151,8 @@ protected ReactiveViewHost( bool attachToRoot, bool performAutoWireup, ResolveStrategy resolveStrategy) - : base(ctx, layoutId, parent, attachToRoot, performAutoWireup, resolveStrategy) - { + : base(ctx, layoutId, parent, attachToRoot, performAutoWireup, resolveStrategy) => SetupRxObjLegacyReflection(); - } /// public event PropertyChangedEventHandler? PropertyChanged; @@ -168,12 +177,14 @@ public TViewModel? ViewModel /// [IgnoreDataMember] [JsonIgnore] - public IObservable>> Changing => this.GetChangingObservable(); + public IObservable>> Changing => + this.GetChangingObservable(); /// [IgnoreDataMember] [JsonIgnore] - public IObservable>> Changed => this.GetChangedObservable(); + public IObservable>> Changed => + this.GetChangedObservable(); /// /// Gets an observable of exceptions thrown during reactive operations on this instance. @@ -214,11 +225,7 @@ public TViewModel? ViewModel /// /// This method intentionally does not touch to avoid reflection and allocations. /// - private void SetupRxObjAot() - { - // No reflection-based property caching in AOT-safe paths. - allPublicProperties = null; - } + private void SetupRxObjAot() => allPublicProperties = null; /// /// Initializes legacy reflection metadata used by older auto-wireup infrastructure. @@ -228,9 +235,6 @@ private void SetupRxObjAot() /// [RequiresUnreferencedCode("This method uses reflection to enumerate public instance properties.")] [RequiresDynamicCode("This method uses reflection to enumerate public instance properties.")] - private void SetupRxObjLegacyReflection() - { - allPublicProperties = new Lazy( - () => GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)); - } + private void SetupRxObjLegacyReflection() => + allPublicProperties = new(() => GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)); } diff --git a/src/ReactiveUI/Platforms/android/ResolveStrategy.cs b/src/ReactiveUI/Platforms/android/ResolveStrategy.cs index 69340120db..f6d70bd617 100644 --- a/src/ReactiveUI/Platforms/android/ResolveStrategy.cs +++ b/src/ReactiveUI/Platforms/android/ResolveStrategy.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/android/SharedPreferencesExtensions.cs b/src/ReactiveUI/Platforms/android/SharedPreferencesExtensions.cs index e5e9ab6cf9..fef0bd0228 100644 --- a/src/ReactiveUI/Platforms/android/SharedPreferencesExtensions.cs +++ b/src/ReactiveUI/Platforms/android/SharedPreferencesExtensions.cs @@ -1,10 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.Content; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -17,21 +19,37 @@ public static class SharedPreferencesExtensions /// /// The observable sequence of keys for changed shared preferences. /// The shared preferences to get the changes from. - public static IObservable PreferenceChanged(this ISharedPreferences sharedPreferences) => // TODO: Create Test - Observable.Create(observer => + public static IObservable + PreferenceChanged(this ISharedPreferences sharedPreferences) => + new PreferenceChangedObservable(sharedPreferences); + + /// + /// Registers a change listener on subscribe and surfaces each changed key — replacing Observable.Create. + /// The listener is unregistered when the subscription is disposed. + /// + /// The shared preferences to observe. + private sealed class PreferenceChangedObservable(ISharedPreferences sharedPreferences) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) { - var listener = new OnSharedPreferenceChangeListener(observer); + ArgumentExceptionHelper.ThrowIfNull(observer); + OnSharedPreferenceChangeListener listener = new(observer); sharedPreferences.RegisterOnSharedPreferenceChangeListener(listener); - return Disposable.Create(() => sharedPreferences.UnregisterOnSharedPreferenceChangeListener(listener)); - }); + return new ActionDisposable(() => sharedPreferences.UnregisterOnSharedPreferenceChangeListener(listener)); + } + } /// /// Private implementation of ISharedPreferencesOnSharedPreferenceChangeListener. /// - private class OnSharedPreferenceChangeListener(IObserver observer) - : Java.Lang.Object, + private sealed class OnSharedPreferenceChangeListener(IObserver observer) + : Java.Lang.Object, ISharedPreferencesOnSharedPreferenceChangeListener { - void ISharedPreferencesOnSharedPreferenceChangeListener.OnSharedPreferenceChanged(ISharedPreferences? sharedPreferences, string? key) => observer.OnNext(key); + /// + void ISharedPreferencesOnSharedPreferenceChangeListener.OnSharedPreferenceChanged( + ISharedPreferences? sharedPreferences, + string? key) => observer.OnNext(key); } } diff --git a/src/ReactiveUI/Platforms/android/UsbManagerExtensions.cs b/src/ReactiveUI/Platforms/android/UsbManagerExtensions.cs index 29143ffbac..cb26792f0b 100644 --- a/src/ReactiveUI/Platforms/android/UsbManagerExtensions.cs +++ b/src/ReactiveUI/Platforms/android/UsbManagerExtensions.cs @@ -1,14 +1,15 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Runtime.Versioning; - using Android.App; using Android.Content; using Android.Hardware.Usb; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -16,6 +17,9 @@ namespace ReactiveUI; /// public static class UsbManagerExtensions { + /// + /// The intent action used when requesting USB permission. + /// private const string ActionUsbPermission = "com.reactiveui.USB_PERMISSION"; /// @@ -26,17 +30,9 @@ public static class UsbManagerExtensions /// The UsbManager system service. /// The Context to request the permission from. /// The UsbDevice to request permission for. - public static IObservable PermissionRequested(this UsbManager manager, Context context, UsbDevice device) => // TODO: Create Test - Observable.Create(observer => - { - var usbPermissionReceiver = new UsbDevicePermissionReceiver(observer, device); - context.RegisterReceiver(usbPermissionReceiver, new IntentFilter(ActionUsbPermission)); - - var intent = PendingIntent.GetBroadcast(context, 0, new Intent(ActionUsbPermission), 0); - manager.RequestPermission(device, intent); - - return Disposable.Create(() => context.UnregisterReceiver(usbPermissionReceiver)); - }); + public static IObservable + PermissionRequested(this UsbManager manager, Context context, UsbDevice device) => + new DevicePermissionObservable(manager, context, device); /// /// Requests temporary permission for the given package to access the accessory. @@ -46,24 +42,73 @@ public static IObservable PermissionRequested(this UsbManager manager, Con /// The UsbManager system service. /// The Context to request the permission from. /// The UsbAccessory to request permission for. - public static IObservable PermissionRequested(this UsbManager manager, Context context, UsbAccessory accessory) => // TODO: Create Test - Observable.Create(observer => + public static IObservable PermissionRequested( + this UsbManager manager, + Context context, + UsbAccessory accessory) => + new AccessoryPermissionObservable(manager, context, accessory); + + /// + /// Requests USB device permission on subscribe and surfaces the granted result — replacing Observable.Create. + /// The broadcast receiver is unregistered when the subscription is disposed. + /// + /// The USB manager system service. + /// The context to request the permission from. + /// The USB device to request permission for. + private sealed class DevicePermissionObservable(UsbManager manager, Context context, UsbDevice device) + : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + UsbDevicePermissionReceiver usbPermissionReceiver = new(observer, device); + context.RegisterReceiver(usbPermissionReceiver, new(ActionUsbPermission)); + + var intent = PendingIntent.GetBroadcast(context, 0, new(ActionUsbPermission), 0); + manager.RequestPermission(device, intent); + + return new ActionDisposable(() => context.UnregisterReceiver(usbPermissionReceiver)); + } + } + + /// + /// Requests USB accessory permission on subscribe and surfaces the granted result — replacing Observable.Create. + /// The broadcast receiver is unregistered when the subscription is disposed. + /// + /// The USB manager system service. + /// The context to request the permission from. + /// The USB accessory to request permission for. + private sealed class AccessoryPermissionObservable(UsbManager manager, Context context, UsbAccessory accessory) + : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) { - var usbPermissionReceiver = new UsbAccessoryPermissionReceiver(observer, accessory); - context.RegisterReceiver(usbPermissionReceiver, new IntentFilter(ActionUsbPermission)); + ArgumentExceptionHelper.ThrowIfNull(observer); - var intent = PendingIntent.GetBroadcast(context, 0, new Intent(ActionUsbPermission), 0); + UsbAccessoryPermissionReceiver usbPermissionReceiver = new(observer, accessory); + context.RegisterReceiver(usbPermissionReceiver, new(ActionUsbPermission)); + + var intent = PendingIntent.GetBroadcast(context, 0, new(ActionUsbPermission), 0); manager.RequestPermission(accessory, intent); - return Disposable.Create(() => context.UnregisterReceiver(usbPermissionReceiver)); - }); + return new ActionDisposable(() => context.UnregisterReceiver(usbPermissionReceiver)); + } + } /// /// Private implementation of BroadcastReceiver to handle device permission requests. /// - private class UsbDevicePermissionReceiver(IObserver observer, UsbDevice device) - : BroadcastReceiver + private sealed class UsbDevicePermissionReceiver(IObserver observer, UsbDevice device) + : BroadcastReceiver { + /// + /// Handles the broadcast for a USB device permission result. + /// + /// The context in which the receiver is running. + /// The intent containing the permission result. [ObsoletedOSPlatform("android33.0")] public override void OnReceive(Context? context, Intent? intent) { @@ -87,9 +132,14 @@ public override void OnReceive(Context? context, Intent? intent) /// /// Private implementation of BroadcastReceiver to handle accessory permission requests. /// - private class UsbAccessoryPermissionReceiver(IObserver observer, UsbAccessory accessory) - : BroadcastReceiver + private sealed class UsbAccessoryPermissionReceiver(IObserver observer, UsbAccessory accessory) + : BroadcastReceiver { + /// + /// Handles the broadcast for a USB accessory permission result. + /// + /// The context in which the receiver is running. + /// The intent containing the permission result. [ObsoletedOSPlatform("android33.0")] public override void OnReceive(Context? context, Intent? intent) { diff --git a/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs b/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs index a85eec889f..f52741a73e 100644 --- a/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs +++ b/src/ReactiveUI/Platforms/android/ViewCommandExtensions.cs @@ -1,13 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Windows.Input; - using Android.Views; -using ReactiveUI.Helpers; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -26,12 +25,12 @@ public static class ViewCommandExtensions /// The view whose click event will trigger the command. Cannot be null. /// An that, when disposed, detaches the event handlers and unbinds the command from the /// view. - public static IDisposable BindToTarget(this ICommand command, View control) // TODO: Create Test + public static IDisposable BindToTarget(this ICommand command, View control) { ArgumentExceptionHelper.ThrowIfNull(command); ArgumentExceptionHelper.ThrowIfNull(control); - var ev = new EventHandler((o, e) => + EventHandler ev = (_, _) => { if (!command.CanExecute(null)) { @@ -39,16 +38,16 @@ public static IDisposable BindToTarget(this ICommand command, View control) // T } command.Execute(null); - }); + }; - var cech = new EventHandler((o, e) => control.Enabled = command.CanExecute(null)); + EventHandler cech = (_, _) => control.Enabled = command.CanExecute(null); command.CanExecuteChanged += cech; control.Click += ev; control.Enabled = command.CanExecute(null); - return Disposable.Create(() => + return new ActionDisposable(() => { command.CanExecuteChanged -= cech; control.Click -= ev; diff --git a/src/ReactiveUI/Platforms/android/ViewMixins.cs b/src/ReactiveUI/Platforms/android/ViewMixins.cs index 4134f3b1c6..5d88d78455 100644 --- a/src/ReactiveUI/Platforms/android/ViewMixins.cs +++ b/src/ReactiveUI/Platforms/android/ViewMixins.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using Android.Views; +using Object = Java.Lang.Object; namespace ReactiveUI; @@ -15,6 +16,9 @@ namespace ReactiveUI; /// is static and cannot be instantiated. public static class ViewMixins { + /// + /// The tag key used to store the view host instance on a view. + /// internal const int ViewHostTag = -4222; /// @@ -24,7 +28,11 @@ public static class ViewMixins /// The view from which to retrieve the associated view host. Cannot be null. /// An instance of if a view host of the specified type is associated with the view; /// otherwise, the default value for . - public static T GetViewHost(this View item) // TODO: Create Test + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public static T GetViewHost(this View item) where T : ILayoutViewHost { var tagData = item?.GetTag(ViewHostTag); @@ -42,6 +50,5 @@ public static T GetViewHost(this View item) // TODO: Create Test /// The view from which to retrieve the associated layout view host. Cannot be null. /// An object that implements if the view has an associated host; otherwise, . - public static ILayoutViewHost? GetViewHost(this View item) => // TODO: Create Test - item?.GetTag(ViewHostTag)?.ToNetObject(); + public static ILayoutViewHost? GetViewHost(this View item) => item?.GetTag(ViewHostTag)?.ToNetObject(); } diff --git a/src/ReactiveUI/Platforms/android/WireUpResourceAttribute.cs b/src/ReactiveUI/Platforms/android/WireUpResourceAttribute.cs index 3bb014fa03..76e43f2ac2 100644 --- a/src/ReactiveUI/Platforms/android/WireUpResourceAttribute.cs +++ b/src/ReactiveUI/Platforms/android/WireUpResourceAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -25,8 +25,8 @@ public WireUpResourceAttribute() /// /// Initializes a new instance of the class. /// - /// Name of the resource. - public WireUpResourceAttribute(string? resourceName) => ResourceNameOverride = resourceName; + /// Name of the resource. + public WireUpResourceAttribute(string? resourceNameOverride) => ResourceNameOverride = resourceNameOverride; /// /// Gets the resource name override. diff --git a/src/ReactiveUI/Platforms/apple-common/AppSupportJsonSuspensionDriver.cs b/src/ReactiveUI/Platforms/apple-common/AppSupportJsonSuspensionDriver.cs index d617b6107b..81fbb7ee07 100644 --- a/src/ReactiveUI/Platforms/apple-common/AppSupportJsonSuspensionDriver.cs +++ b/src/ReactiveUI/Platforms/apple-common/AppSupportJsonSuspensionDriver.cs @@ -10,6 +10,8 @@ using Foundation; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -62,7 +64,7 @@ public sealed class AppSupportJsonSuspensionDriver : ISuspensionDriver /// Thrown when is . public AppSupportJsonSuspensionDriver(string subDirectory = DefaultSubDirectory) { - ArgumentNullException.ThrowIfNull(subDirectory); + ArgumentExceptionHelper.ThrowIfNull(subDirectory); _appDirectory = new Lazy( () => CreateAppDirectory(NSSearchPathDirectory.ApplicationSupportDirectory, subDirectory), @@ -72,7 +74,7 @@ public AppSupportJsonSuspensionDriver(string subDirectory = DefaultSubDirectory) /// public IObservable LoadState(JsonTypeInfo typeInfo) { - ArgumentNullException.ThrowIfNull(typeInfo); + ArgumentExceptionHelper.ThrowIfNull(typeInfo); try { @@ -91,7 +93,7 @@ public AppSupportJsonSuspensionDriver(string subDirectory = DefaultSubDirectory) /// public IObservable SaveState(T state, JsonTypeInfo typeInfo) { - ArgumentNullException.ThrowIfNull(typeInfo); + ArgumentExceptionHelper.ThrowIfNull(typeInfo); try { @@ -99,7 +101,7 @@ public IObservable SaveState(T state, JsonTypeInfo typeInfo) using var stream = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None); JsonSerializer.Serialize(stream, state, typeInfo); - return Observables.Unit; + return SingleValueObservable.Unit; } catch (Exception ex) { @@ -138,7 +140,7 @@ public IObservable SaveState(T state) using var stream = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None); JsonSerializer.Serialize(stream, state); - return Observables.Unit; + return SingleValueObservable.Unit; } catch (Exception ex) { @@ -154,7 +156,7 @@ public IObservable InvalidateState() var path = GetStatePath(); File.Delete(path); - return Observables.Unit; + return SingleValueObservable.Unit; } catch (Exception ex) { diff --git a/src/ReactiveUI/Platforms/apple-common/BlockObserveValueDelegate.cs b/src/ReactiveUI/Platforms/apple-common/BlockObserveValueDelegate.cs index cf2a4eb844..aaca9d61f1 100644 --- a/src/ReactiveUI/Platforms/apple-common/BlockObserveValueDelegate.cs +++ b/src/ReactiveUI/Platforms/apple-common/BlockObserveValueDelegate.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeOffsetToNSDateConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeOffsetToNSDateConverter.cs index 0b7210763c..74a9142a36 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeOffsetToNSDateConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeOffsetToNSDateConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeToNSDateConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeToNSDateConverter.cs index 6291c56cd1..e68b7dc687 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeToNSDateConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/DateTimeToNSDateConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeConverter.cs index 0a37b44128..60b6c2d5d3 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeOffsetConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeOffsetConverter.cs index 80c0a11494..26548fbf9b 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeOffsetConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToDateTimeOffsetConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeConverter.cs index 3ee0e99ff3..b630c605b4 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeOffsetConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeOffsetConverter.cs index 7037d15efb..939feacde7 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeOffsetConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/NSDateToNullableDateTimeOffsetConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeOffsetToNSDateConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeOffsetToNSDateConverter.cs index 32f0148efe..a40d6cbdb9 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeOffsetToNSDateConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeOffsetToNSDateConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeToNSDateConverter.cs b/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeToNSDateConverter.cs index a8e43d9e31..35cdbab118 100644 --- a/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeToNSDateConverter.cs +++ b/src/ReactiveUI/Platforms/apple-common/Converters/NullableDateTimeToNSDateConverter.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. #if UIKIT || MACOS -using System.Diagnostics.CodeAnalysis; using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/apple-common/IndexNormalizer.cs b/src/ReactiveUI/Platforms/apple-common/IndexNormalizer.cs index c68d2fffa6..e74c5baf87 100644 --- a/src/ReactiveUI/Platforms/apple-common/IndexNormalizer.cs +++ b/src/ReactiveUI/Platforms/apple-common/IndexNormalizer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs b/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs index 88695ff103..bcb02713c7 100644 --- a/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs +++ b/src/ReactiveUI/Platforms/apple-common/KVOObservableForProperty.cs @@ -19,10 +19,15 @@ public sealed class KVOObservableForProperty : ICreatesObservableForProperty { /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged) { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(propertyName); + ArgumentExceptionHelper.ThrowIfNull(type); + ArgumentExceptionHelper.ThrowIfNull(propertyName); if (!typeof(NSObject).IsAssignableFrom(type)) { @@ -32,18 +37,35 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang return IsDeclaredOnNSObject(type, propertyName) ? 15 : 0; } + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] public IObservable> GetNotificationForProperty( object sender, Expression expression, string propertyName, - bool beforeChanged = false, - bool suppressWarnings = false) + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged, + bool suppressWarnings) { - ArgumentNullException.ThrowIfNull(sender); - ArgumentNullException.ThrowIfNull(expression); - ArgumentNullException.ThrowIfNull(propertyName); + ArgumentExceptionHelper.ThrowIfNull(sender); + ArgumentExceptionHelper.ThrowIfNull(expression); + ArgumentExceptionHelper.ThrowIfNull(propertyName); if (sender is not NSObject) { @@ -101,10 +123,10 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang bool beforeChanged = false, bool suppressWarnings = false) { - ArgumentNullException.ThrowIfNull(sender); - ArgumentNullException.ThrowIfNull(expression); - ArgumentNullException.ThrowIfNull(propertyName); - ArgumentNullException.ThrowIfNull(observationKey); + ArgumentExceptionHelper.ThrowIfNull(sender); + ArgumentExceptionHelper.ThrowIfNull(expression); + ArgumentExceptionHelper.ThrowIfNull(propertyName); + ArgumentExceptionHelper.ThrowIfNull(observationKey); if (sender is not NSObject obj) { @@ -113,7 +135,7 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang return Observable.Create>(observer => { - ArgumentNullException.ThrowIfNull(observer); + ArgumentExceptionHelper.ThrowIfNull(observer); // Create a single stable delegate instance; KVO removal requires the same observer instance. var callback = new BlockObserveValueDelegate((unusedKeyPath, observedObject, unusedChange) => diff --git a/src/ReactiveUI/Platforms/apple-common/NSRunloopScheduler.cs b/src/ReactiveUI/Platforms/apple-common/NSRunloopScheduler.cs index 5ecf9bc1b4..ab9b979ab3 100644 --- a/src/ReactiveUI/Platforms/apple-common/NSRunloopScheduler.cs +++ b/src/ReactiveUI/Platforms/apple-common/NSRunloopScheduler.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs b/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs index 8a6319ab7c..800cd0a9ac 100644 --- a/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs +++ b/src/ReactiveUI/Platforms/apple-common/ObservableForPropertyBase.cs @@ -1,14 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System; using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reactive.Disposables; -using System.Reactive.Linq; using Foundation; @@ -47,7 +42,11 @@ public abstract class ObservableForPropertyBase : ICreatesObservableForProperty /// /// Synchronization gate protecting and . /// + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else private readonly object _gate = new(); + #endif /// /// Configuration map keyed by registered type and then by property name. @@ -70,10 +69,15 @@ public abstract class ObservableForPropertyBase : ICreatesObservableForProperty /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] - public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false) + public int GetAffinityForObject(Type type, string propertyName) => + GetAffinityForObject(type, propertyName, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged) { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(propertyName); + ArgumentExceptionHelper.ThrowIfNull(type); + ArgumentExceptionHelper.ThrowIfNull(propertyName); if (beforeChanged) { @@ -84,14 +88,31 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang return match is null ? 0 : match.Affinity; } + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName) => + GetNotificationForProperty(sender, expression, propertyName, false, false); + + /// + [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] + public IObservable> GetNotificationForProperty( + object sender, + Expression expression, + string propertyName, + bool beforeChanged) => + GetNotificationForProperty(sender, expression, propertyName, beforeChanged, false); + /// [RequiresUnreferencedCode("Uses reflection over runtime types which is not trim- or AOT-safe.")] public IObservable> GetNotificationForProperty( object sender, Expression expression, string propertyName, - bool beforeChanged = false, - bool suppressWarnings = false) + bool beforeChanged, + bool suppressWarnings) { ArgumentExceptionHelper.ThrowIfNull(sender); ArgumentExceptionHelper.ThrowIfNull(expression); @@ -253,9 +274,9 @@ protected void Register( int affinity, Func>> createObservable) { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(property); - ArgumentNullException.ThrowIfNull(createObservable); + ArgumentExceptionHelper.ThrowIfNull(type); + ArgumentExceptionHelper.ThrowIfNull(property); + ArgumentExceptionHelper.ThrowIfNull(createObservable); lock (_gate) { diff --git a/src/ReactiveUI/Platforms/apple-common/PlatformOperations.cs b/src/ReactiveUI/Platforms/apple-common/PlatformOperations.cs index a39325285c..a4b4ecbc5a 100644 --- a/src/ReactiveUI/Platforms/apple-common/PlatformOperations.cs +++ b/src/ReactiveUI/Platforms/apple-common/PlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs index 9a44b28620..1ab62aa276 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveControl.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs index 650dc121fd..c61607a9bf 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveImageView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -164,7 +164,6 @@ protected override void Dispose(bool disposing) /// (i.e. you can call RaiseAndSetIfChanged). /// /// The view model type. -[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Classes with the same class names within.")] public abstract class ReactiveImageView : ReactiveImageView, IViewFor where TViewModel : class { diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveSplitViewController.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveSplitViewController.cs index 2e514b3a51..a3cc518eea 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveSplitViewController.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveSplitViewController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -157,7 +157,6 @@ protected override void Dispose(bool disposing) /// (i.e. you can call RaiseAndSetIfChanged). /// /// The view model type. -[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Classes with the same class names within.")] public abstract class ReactiveSplitViewController : ReactiveSplitViewController, IViewFor where TViewModel : class { diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs index 74f482d503..90d57c5239 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/ReactiveViewController.cs b/src/ReactiveUI/Platforms/apple-common/ReactiveViewController.cs index d4ef0cf6be..60835f89a0 100644 --- a/src/ReactiveUI/Platforms/apple-common/ReactiveViewController.cs +++ b/src/ReactiveUI/Platforms/apple-common/ReactiveViewController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs b/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs index 7b6dee8935..af631a6e15 100644 --- a/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs +++ b/src/ReactiveUI/Platforms/apple-common/TargetActionCommandBinder.cs @@ -6,7 +6,6 @@ #nullable enable using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Windows.Input; @@ -251,8 +250,8 @@ void Handler(object? s, EventArgs e) => where TEventArgs : EventArgs { ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(addHandler); - ArgumentNullException.ThrowIfNull(removeHandler); + ArgumentExceptionHelper.ThrowIfNull(addHandler); + ArgumentExceptionHelper.ThrowIfNull(removeHandler); if (command is null) { diff --git a/src/ReactiveUI/Platforms/apple-common/UIViewControllerMixins.cs b/src/ReactiveUI/Platforms/apple-common/UIViewControllerMixins.cs index f00bd9b3c8..24d3c71af3 100644 --- a/src/ReactiveUI/Platforms/apple-common/UIViewControllerMixins.cs +++ b/src/ReactiveUI/Platforms/apple-common/UIViewControllerMixins.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/Update.cs b/src/ReactiveUI/Platforms/apple-common/Update.cs index 415743508a..aca47b3d76 100644 --- a/src/ReactiveUI/Platforms/apple-common/Update.cs +++ b/src/ReactiveUI/Platforms/apple-common/Update.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/UpdateType.cs b/src/ReactiveUI/Platforms/apple-common/UpdateType.cs index d882c60e85..718603639a 100644 --- a/src/ReactiveUI/Platforms/apple-common/UpdateType.cs +++ b/src/ReactiveUI/Platforms/apple-common/UpdateType.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs b/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs index 4432c70f4f..703377066d 100644 --- a/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs +++ b/src/ReactiveUI/Platforms/apple-common/ViewModelViewHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/ios/LinkerOverrides.cs b/src/ReactiveUI/Platforms/ios/LinkerOverrides.cs index cbcb6b5a04..3f5c06d599 100644 --- a/src/ReactiveUI/Platforms/ios/LinkerOverrides.cs +++ b/src/ReactiveUI/Platforms/ios/LinkerOverrides.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/mac/AppKitObservableForProperty.cs b/src/ReactiveUI/Platforms/mac/AppKitObservableForProperty.cs index bc7ea8bfae..36bfc6320b 100644 --- a/src/ReactiveUI/Platforms/mac/AppKitObservableForProperty.cs +++ b/src/ReactiveUI/Platforms/mac/AppKitObservableForProperty.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs b/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs index 926cbc488b..862723c89e 100644 --- a/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs +++ b/src/ReactiveUI/Platforms/mac/AutoSuspendHelper.cs @@ -3,8 +3,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; using AppKit; @@ -89,7 +87,7 @@ public class AutoSuspendHelper<[DynamicallyAccessedMembers(DynamicallyAccessedMe /// public AutoSuspendHelper(T appDelegate) { - ArgumentNullException.ThrowIfNull(appDelegate); + ArgumentExceptionHelper.ThrowIfNull(appDelegate); // Developer-time guard. Cache the result per delegate runtime type to avoid repeated reflection. EnsureMethodsNotOverloadedCached(); diff --git a/src/ReactiveUI/Platforms/mac/ReactiveWindowController.cs b/src/ReactiveUI/Platforms/mac/ReactiveWindowController.cs index a91e543e96..6157730e01 100644 --- a/src/ReactiveUI/Platforms/mac/ReactiveWindowController.cs +++ b/src/ReactiveUI/Platforms/mac/ReactiveWindowController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/mobile-common/ComponentModelFallbackConverter.cs b/src/ReactiveUI/Platforms/mobile-common/ComponentModelFallbackConverter.cs index da34645706..97f5bd0be3 100644 --- a/src/ReactiveUI/Platforms/mobile-common/ComponentModelFallbackConverter.cs +++ b/src/ReactiveUI/Platforms/mobile-common/ComponentModelFallbackConverter.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Concurrent; +using System.ComponentModel; namespace ReactiveUI; @@ -42,36 +43,36 @@ public sealed class ComponentModelFallbackConverter : IBindingFallbackConverter "ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The callers of this method ensure getting the converter is trim compatible - i.e. the type is not Nullable.")] - public int GetAffinityForObjects([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) + public int GetAffinityForObjects( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) { ArgumentExceptionHelper.ThrowIfNull(fromType); ArgumentExceptionHelper.ThrowIfNull(toType); - // Return cached capability or compute and cache - var canConvert = _capabilityCache.GetOrAdd((fromType, toType), static key => + var canConvert = _capabilityCache.GetOrAdd((fromType, toType), CapabilityFactory); + + return canConvert ? 1 : 0; + + static bool CapabilityFactory((Type From, Type To) key) { try { - // Check if component model can convert this pair. - // String is a special case: for string→T conversions, use the target type's converter - // and check CanConvertFrom(string) rather than CanConvertTo(string). - // CanConvertTo(string) returns true for nearly all TypeConverters, causing false - // positives that lead to NotSupportedException at conversion time. - var (lookupFrom, lookupTo) = key.From == typeof(string) ? (key.To, key.From) : (key.From, key.To); + var fromIsString = key.From == typeof(string); + var (lookupFrom, lookupTo) = fromIsString ? (key.To, key.From) : (key.From, key.To); var converter = TypeDescriptor.GetConverter(lookupFrom); - return key.From == typeof(string) - ? converter?.CanConvertFrom(typeof(string)) == true - : converter?.CanConvertTo(lookupTo) == true; + if (fromIsString) + { + return converter?.CanConvertFrom(typeof(string)) == true; + } + + return converter?.CanConvertTo(lookupTo) == true; } catch { - // Component model threw - assume cannot convert return false; } - }); - - // Affinity 1 = last resort (only when nothing else works) - return canConvert ? 1 : 0; + } } /// @@ -79,14 +80,18 @@ public int GetAffinityForObjects([DynamicallyAccessedMembers(DynamicallyAccessed "ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The callers of this method ensure getting the converter is trim compatible - i.e. the type is not Nullable.")] - public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, object from, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, object? conversionHint, [NotNullWhen(true)] out object? result) + public bool TryConvert( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + object from, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, + object? conversionHint, + [NotNullWhen(true)] out object? result) { ArgumentExceptionHelper.ThrowIfNull(from); ArgumentExceptionHelper.ThrowIfNull(toType); try { - // Get or create cached converter for this pair var converter = GetConverter(fromType, toType); if (converter is null) { @@ -95,9 +100,7 @@ public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberType return false; } - // Perform conversion - // String is a special case: use ConvertFrom for string→T conversions - var converted = (fromType == typeof(string)) + var converted = fromType == typeof(string) ? converter.ConvertFrom(from) : converter.ConvertTo(from, toType); @@ -107,28 +110,36 @@ public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberType return true; } - // Conversion returned null - treat as failure result = null; return false; } catch (FormatException ex) { - // Format exception = conversion failed (expected for invalid input) - this.Log().Debug(ex, "Component model conversion failed (FormatException) for {0} -> {1}", fromType, toType); + this.Log().Debug( + ex, + "Component model conversion failed (FormatException) for {0} -> {1}", + fromType, + toType); result = null; return false; } catch (Exception ex) when (ex.InnerException is FormatException or IndexOutOfRangeException) { - // Component model often wraps format exceptions - this.Log().Debug(ex, "Component model conversion failed (wrapped exception) for {0} -> {1}", fromType, toType); + this.Log().Debug( + ex, + "Component model conversion failed (wrapped exception) for {0} -> {1}", + fromType, + toType); result = null; return false; } catch (Exception ex) { - // Unexpected exception - log as warning and treat as failure - this.Log().Warn(ex, "Component model conversion threw unexpected exception for {0} -> {1}", fromType, toType); + this.Log().Warn( + ex, + "Component model conversion threw unexpected exception for {0} -> {1}", + fromType, + toType); result = null; return false; } @@ -146,19 +157,23 @@ public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberType "ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The callers of this method ensure getting the converter is trim compatible - i.e. the type is not Nullable.")] - private static TypeConverter? GetConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) + private static TypeConverter? GetConverter( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) { - // String is a special case: for string→T conversions use the target's converter and ConvertFrom. - // Check CanConvertFrom(string) to avoid false positives from CanConvertTo(string), - // which is true for nearly all TypeConverters and would cause NotSupportedException. - var lookupFrom = fromType == typeof(string) ? toType : fromType; + var fromIsString = fromType == typeof(string); + var lookupFrom = fromIsString ? toType : fromType; + + return _converterCache.GetOrAdd((fromType, toType), ConverterFactory); - return _converterCache.GetOrAdd((fromType, toType), _ => + TypeConverter? ConverterFactory((Type From, Type To) key) { var converter = TypeDescriptor.GetConverter(lookupFrom); - return fromType == typeof(string) - ? (converter?.CanConvertFrom(typeof(string)) == true ? converter : null) - : (converter?.CanConvertTo(toType) == true ? converter : null); - }); + var canConvert = fromIsString + ? converter?.CanConvertFrom(typeof(string)) == true + : converter?.CanConvertTo(toType) == true; + + return canConvert ? converter : null; + } } } diff --git a/src/ReactiveUI/Platforms/net/ComponentModelFallbackConverter.cs b/src/ReactiveUI/Platforms/net/ComponentModelFallbackConverter.cs index 9f82e09637..4e43aec5bb 100644 --- a/src/ReactiveUI/Platforms/net/ComponentModelFallbackConverter.cs +++ b/src/ReactiveUI/Platforms/net/ComponentModelFallbackConverter.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Concurrent; +using System.ComponentModel; namespace ReactiveUI; @@ -41,21 +42,17 @@ public sealed class ComponentModelFallbackConverter : IBindingFallbackConverter "ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The callers of this method ensure getting the converter is trim compatible - i.e. the type is not Nullable.")] - public int GetAffinityForObjects([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) + public int GetAffinityForObjects( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) { ArgumentExceptionHelper.ThrowIfNull(fromType); ArgumentExceptionHelper.ThrowIfNull(toType); - // Return cached capability or compute and cache var canConvert = _capabilityCache.GetOrAdd((fromType, toType), static key => { try { - // Check if component model can convert this pair. - // String is a special case: for string→T conversions, use the target type's converter - // and check CanConvertFrom(string) rather than CanConvertTo(string). - // CanConvertTo(string) returns true for nearly all TypeConverters, causing false - // positives that lead to NotSupportedException at conversion time. var (lookupFrom, lookupTo) = key.From == typeof(string) ? (key.To, key.From) : (key.From, key.To); var converter = TypeDescriptor.GetConverter(lookupFrom); return key.From == typeof(string) @@ -64,17 +61,20 @@ public int GetAffinityForObjects([DynamicallyAccessedMembers(DynamicallyAccessed } catch { - // Component model threw - assume cannot convert return false; } }); - // Affinity 1 = last resort (only when nothing else works) return canConvert ? 1 : 0; } /// - public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, object from, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, object? conversionHint, [NotNullWhen(true)] out object? result) + public bool TryConvert( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + object from, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType, + object? conversionHint, + [NotNullWhen(true)] out object? result) { ArgumentExceptionHelper.ThrowIfNull(fromType); ArgumentExceptionHelper.ThrowIfNull(from); @@ -82,7 +82,6 @@ public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberType try { - // Get or create cached converter for this pair var converter = GetConverter(fromType, toType); if (converter is null) { @@ -91,8 +90,6 @@ public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberType return false; } - // Perform conversion - // String is a special case: use ConvertFrom for string→T conversions var converted = (fromType == typeof(string)) ? converter.ConvertFrom(from) : converter.ConvertTo(from, toType); @@ -103,28 +100,36 @@ public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberType return true; } - // Conversion returned null - treat as failure result = null; return false; } catch (FormatException ex) { - // Format exception = conversion failed (expected for invalid input) - this.Log().Debug(ex, "Component model conversion failed (FormatException) for {0} -> {1}", fromType, toType); + this.Log().Debug( + ex, + "Component model conversion failed (FormatException) for {0} -> {1}", + fromType, + toType); result = null; return false; } catch (Exception ex) when (ex.InnerException is FormatException or IndexOutOfRangeException) { - // Component model often wraps format exceptions - this.Log().Debug(ex, "Component model conversion failed (wrapped exception) for {0} -> {1}", fromType, toType); + this.Log().Debug( + ex, + "Component model conversion failed (wrapped exception) for {0} -> {1}", + fromType, + toType); result = null; return false; } catch (Exception ex) { - // Unexpected exception - log as warning and treat as failure - this.Log().Warn(ex, "Component model conversion threw unexpected exception for {0} -> {1}", fromType, toType); + this.Log().Warn( + ex, + "Component model conversion threw unexpected exception for {0} -> {1}", + fromType, + toType); result = null; return false; } @@ -142,19 +147,21 @@ public bool TryConvert([DynamicallyAccessedMembers(DynamicallyAccessedMemberType "ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The callers of this method ensure getting the converter is trim compatible - i.e. the type is not Nullable.")] - private static TypeConverter? GetConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) + private static TypeConverter? GetConverter( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type fromType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type toType) { - // String is a special case: for string→T conversions use the target's converter and ConvertFrom. - // Check CanConvertFrom(string) to avoid false positives from CanConvertTo(string), - // which is true for nearly all TypeConverters and would cause NotSupportedException. var lookupFrom = fromType == typeof(string) ? toType : fromType; return _converterCache.GetOrAdd((fromType, toType), _ => { var converter = TypeDescriptor.GetConverter(lookupFrom); - return fromType == typeof(string) - ? (converter?.CanConvertFrom(typeof(string)) == true ? converter : null) - : (converter?.CanConvertTo(toType) == true ? converter : null); + if (fromType == typeof(string)) + { + return converter.CanConvertFrom(typeof(string)) ? converter : null; + } + + return converter.CanConvertTo(toType) ? converter : null; }); } } diff --git a/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs index f9077f5978..3a1ea1ba1a 100644 --- a/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/net/PlatformRegistrations.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2022 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// + +using System.Reactive.Concurrency; namespace ReactiveUI; @@ -19,10 +20,12 @@ public void Register(IRegistrar registrar) registrar.RegisterConstant(static () => new ComponentModelFallbackConverter()); - if (!ModeDetector.InUnitTestRunner()) + if (ModeDetector.InUnitTestRunner()) { - RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; - RxSchedulers.MainThreadScheduler = DefaultScheduler.Instance; + return; } + + RxSchedulers.TaskpoolScheduler = TaskPoolScheduler.Default; + RxSchedulers.MainThreadScheduler = DefaultScheduler.Instance; } } diff --git a/src/ReactiveUI/Platforms/tizen/EcoreMainloopScheduler.cs b/src/ReactiveUI/Platforms/tizen/EcoreMainloopScheduler.cs index b8ead616f0..04bf5d8228 100644 --- a/src/ReactiveUI/Platforms/tizen/EcoreMainloopScheduler.cs +++ b/src/ReactiveUI/Platforms/tizen/EcoreMainloopScheduler.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/tizen/PlatformOperations.cs b/src/ReactiveUI/Platforms/tizen/PlatformOperations.cs index ed153ce7ef..27c7dcde4a 100644 --- a/src/ReactiveUI/Platforms/tizen/PlatformOperations.cs +++ b/src/ReactiveUI/Platforms/tizen/PlatformOperations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs b/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs index ecb19b4cae..f760df0cd4 100644 --- a/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs +++ b/src/ReactiveUI/Platforms/tizen/PlatformRegistrations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/tvos/LinkerOverrides.cs b/src/ReactiveUI/Platforms/tvos/LinkerOverrides.cs index 3f05400c75..8721016961 100644 --- a/src/ReactiveUI/Platforms/tvos/LinkerOverrides.cs +++ b/src/ReactiveUI/Platforms/tvos/LinkerOverrides.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/tvos/UIKitObservableForProperty.cs b/src/ReactiveUI/Platforms/tvos/UIKitObservableForProperty.cs index 475d9abb71..c1e55f90a4 100644 --- a/src/ReactiveUI/Platforms/tvos/UIKitObservableForProperty.cs +++ b/src/ReactiveUI/Platforms/tvos/UIKitObservableForProperty.cs @@ -1,9 +1,8 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System; using UIKit; diff --git a/src/ReactiveUI/Platforms/uikit-common/CollectionViewSectionInformation.cs b/src/ReactiveUI/Platforms/uikit-common/CollectionViewSectionInformation.cs index 142a36e563..d9ead22350 100644 --- a/src/ReactiveUI/Platforms/uikit-common/CollectionViewSectionInformation.cs +++ b/src/ReactiveUI/Platforms/uikit-common/CollectionViewSectionInformation.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs b/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs index a6b417575b..19b637e24c 100644 --- a/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs +++ b/src/ReactiveUI/Platforms/uikit-common/CommonReactiveSource.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -6,12 +6,8 @@ using System.Collections; using System.Collections.Specialized; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Globalization; -using DynamicData; -using DynamicData.Binding; - using Foundation; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs b/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs index e5fd4e884b..e8c426f6ed 100644 --- a/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs +++ b/src/ReactiveUI/Platforms/uikit-common/FlexibleCommandBinder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -149,8 +149,8 @@ public abstract class FlexibleCommandBinder : ICreatesCommandBinding where TEventArgs : EventArgs { ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(addHandler); - ArgumentNullException.ThrowIfNull(removeHandler); + ArgumentExceptionHelper.ThrowIfNull(addHandler); + ArgumentExceptionHelper.ThrowIfNull(removeHandler); // Match existing binder behavior: null command means "no binding". if (command is null) @@ -211,8 +211,8 @@ protected static IDisposable ForEvent( { ArgumentExceptionHelper.ThrowIfNull(command); ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(addHandler); - ArgumentNullException.ThrowIfNull(removeHandler); + ArgumentExceptionHelper.ThrowIfNull(addHandler); + ArgumentExceptionHelper.ThrowIfNull(removeHandler); ArgumentExceptionHelper.ThrowIfNull(enabledProperty); commandParameter ??= Observable.Return((object?)target); @@ -287,8 +287,8 @@ protected static IDisposable ForEvent( { ArgumentExceptionHelper.ThrowIfNull(command); ArgumentExceptionHelper.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(addHandler); - ArgumentNullException.ThrowIfNull(removeHandler); + ArgumentExceptionHelper.ThrowIfNull(addHandler); + ArgumentExceptionHelper.ThrowIfNull(removeHandler); ArgumentExceptionHelper.ThrowIfNull(enabledProperty); commandParameter ??= Observable.Return((object?)target); @@ -473,8 +473,8 @@ void CanExecuteHandler(object? s, EventArgs e) => /// Thrown when or is null. protected void Register(Type type, int affinity, Func, IDisposable> createBinding) { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(createBinding); + ArgumentExceptionHelper.ThrowIfNull(type); + ArgumentExceptionHelper.ThrowIfNull(createBinding); lock (_gate) { diff --git a/src/ReactiveUI/Platforms/uikit-common/ISectionInformation.cs b/src/ReactiveUI/Platforms/uikit-common/ISectionInformation.cs index 0288712a61..c244ee4a55 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ISectionInformation.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ISectionInformation.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/IUICollViewAdapter.cs b/src/ReactiveUI/Platforms/uikit-common/IUICollViewAdapter.cs index 62dca7a05c..764ae08759 100644 --- a/src/ReactiveUI/Platforms/uikit-common/IUICollViewAdapter.cs +++ b/src/ReactiveUI/Platforms/uikit-common/IUICollViewAdapter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionReusableView.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionReusableView.cs index 164f9fe810..323666d67e 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionReusableView.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionReusableView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs index 959db7fab0..6d97e8ab52 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewCell.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewCell.cs index 34e8ab95e1..effade0b71 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewCell.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewCell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewController.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewController.cs index 22f234c397..55a7f0f66f 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewController.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs index 047535d9c3..4cbc28ee0a 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSource.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs index 7a0d17e2a5..e38c58a68c 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveCollectionViewSourceExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveNavigationController.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveNavigationController.cs index 496227b568..e39080d184 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveNavigationController.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveNavigationController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactivePageViewController.cs b/src/ReactiveUI/Platforms/uikit-common/ReactivePageViewController.cs index 66f1b08de5..b73ab0e363 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactivePageViewController.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactivePageViewController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTabBarController.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTabBarController.cs index c507e07226..e010ae0534 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTabBarController.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTabBarController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs index 20457b441f..32240fc799 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableView.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewCell.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewCell.cs index 1c7ba07592..00cfc4d617 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewCell.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewCell.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewController.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewController.cs index bb4c40e714..ad19e35bf0 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewController.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs index 5562d96990..d96230b55e 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSource.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs index 9d50e757f0..1691ae55ff 100644 --- a/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs +++ b/src/ReactiveUI/Platforms/uikit-common/ReactiveTableViewSourceExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs b/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs index 1ebab7cc3c..48ff306181 100644 --- a/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs +++ b/src/ReactiveUI/Platforms/uikit-common/RoutedViewHost.cs @@ -1,12 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Specialized; -using DynamicData.Binding; - using NSViewController = UIKit.UIViewController; namespace ReactiveUI; diff --git a/src/ReactiveUI/Platforms/uikit-common/SectionInfoIdGenerator.cs b/src/ReactiveUI/Platforms/uikit-common/SectionInfoIdGenerator.cs index 3cab1bdb3b..29f122f586 100644 --- a/src/ReactiveUI/Platforms/uikit-common/SectionInfoIdGenerator.cs +++ b/src/ReactiveUI/Platforms/uikit-common/SectionInfoIdGenerator.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/TableSectionHeader.cs b/src/ReactiveUI/Platforms/uikit-common/TableSectionHeader.cs index 4dfad72613..1e1a487cb5 100644 --- a/src/ReactiveUI/Platforms/uikit-common/TableSectionHeader.cs +++ b/src/ReactiveUI/Platforms/uikit-common/TableSectionHeader.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/TableSectionInformation.cs b/src/ReactiveUI/Platforms/uikit-common/TableSectionInformation.cs index 47ca75de44..ec349d393f 100644 --- a/src/ReactiveUI/Platforms/uikit-common/TableSectionInformation.cs +++ b/src/ReactiveUI/Platforms/uikit-common/TableSectionInformation.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs b/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs index d3a79057bb..9b30242b96 100644 --- a/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs +++ b/src/ReactiveUI/Platforms/uikit-common/UICollectionViewAdapter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs b/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs index e85190f502..55d338fecd 100644 --- a/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs +++ b/src/ReactiveUI/Platforms/uikit-common/UITableViewAdapter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/Polyfills/CallerArgumentExpressionAttribute.cs b/src/ReactiveUI/Polyfills/CallerArgumentExpressionAttribute.cs index 2914c4488f..6a731a3882 100644 --- a/src/ReactiveUI/Polyfills/CallerArgumentExpressionAttribute.cs +++ b/src/ReactiveUI/Polyfills/CallerArgumentExpressionAttribute.cs @@ -1,14 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NET5_0_OR_GREATER - using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.CompilerServices; diff --git a/src/ReactiveUI/Polyfills/DoesNotReturnIfAttribute.cs b/src/ReactiveUI/Polyfills/DoesNotReturnIfAttribute.cs index 109174f661..b28c2b3215 100644 --- a/src/ReactiveUI/Polyfills/DoesNotReturnIfAttribute.cs +++ b/src/ReactiveUI/Polyfills/DoesNotReturnIfAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER - using System.Diagnostics; namespace System.Diagnostics.CodeAnalysis; @@ -40,5 +37,5 @@ public DoesNotReturnIfAttribute(bool parameterValue) => #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute))] +[assembly: TypeForwardedTo(typeof(DoesNotReturnIfAttribute))] #endif diff --git a/src/ReactiveUI/Polyfills/DynamicallyAccessedMemberTypes.cs b/src/ReactiveUI/Polyfills/DynamicallyAccessedMemberTypes.cs index aef5a6f789..c1d26b1577 100644 --- a/src/ReactiveUI/Polyfills/DynamicallyAccessedMemberTypes.cs +++ b/src/ReactiveUI/Polyfills/DynamicallyAccessedMemberTypes.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NET - namespace System.Diagnostics.CodeAnalysis; /// @@ -16,6 +13,10 @@ namespace System.Diagnostics.CodeAnalysis; /// bitwise combination of its member values. /// [Flags] +[SuppressMessage( + "Major Code Smell", + "S4070:Non-flags enums should not be marked with FlagsAttribute", + Justification = "Faithful BCL polyfill.")] internal enum DynamicallyAccessedMemberTypes { /// @@ -31,6 +32,10 @@ internal enum DynamicallyAccessedMemberTypes /// /// Specifies all public constructors. /// + [SuppressMessage( + "Roslynator", + "RCS1157:Composite enum value contains undefined flag", + Justification = "Faithful BCL polyfill.")] PublicConstructors = 0x0002 | PublicParameterlessConstructor, /// @@ -102,5 +107,5 @@ internal enum DynamicallyAccessedMemberTypes #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes))] +[assembly: TypeForwardedTo(typeof(DynamicallyAccessedMemberTypes))] #endif diff --git a/src/ReactiveUI/Polyfills/DynamicallyAccessedMembersAttribute.cs b/src/ReactiveUI/Polyfills/DynamicallyAccessedMembersAttribute.cs index d25f053604..7b93d441af 100644 --- a/src/ReactiveUI/Polyfills/DynamicallyAccessedMembersAttribute.cs +++ b/src/ReactiveUI/Polyfills/DynamicallyAccessedMembersAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NET - using System.Diagnostics; namespace System.Diagnostics.CodeAnalysis; @@ -50,5 +47,5 @@ public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes member #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute))] +[assembly: TypeForwardedTo(typeof(DynamicallyAccessedMembersAttribute))] #endif diff --git a/src/ReactiveUI/Polyfills/HashCode.cs b/src/ReactiveUI/Polyfills/HashCode.cs new file mode 100644 index 0000000000..64b2894b9d --- /dev/null +++ b/src/ReactiveUI/Polyfills/HashCode.cs @@ -0,0 +1,131 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +// Minimal polyfill for the static System.HashCode.Combine overloads on .NET Framework, which lacks System.HashCode. +// Only the static Combine methods are used in this assembly, so the value combination is delegated to ValueTuple's +// own hash combination rather than reimplementing the xxHash32 accumulator. +#if NETFRAMEWORK + +namespace System; + +/// +/// Combines the hash codes of multiple values into a single hash code. +/// +internal static class HashCode +{ + /// Combines a single value into a hash code. + /// The type of the first value. + /// The first value. + /// The combined hash code. + public static int Combine(T1 value1) => value1?.GetHashCode() ?? 0; + + /// Combines two values into a hash code. + /// The type of the first value. + /// The type of the second value. + /// The first value. + /// The second value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2) => + (value1, value2).GetHashCode(); + + /// Combines three values into a hash code. + /// The type of the first value. + /// The type of the second value. + /// The type of the third value. + /// The first value. + /// The second value. + /// The third value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3) => + (value1, value2, value3).GetHashCode(); + + /// Combines four values into a hash code. + /// The type of the first value. + /// The type of the second value. + /// The type of the third value. + /// The type of the fourth value. + /// The first value. + /// The second value. + /// The third value. + /// The fourth value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) => + (value1, value2, value3, value4).GetHashCode(); + + /// Combines five values into a hash code. + /// The type of the first value. + /// The type of the second value. + /// The type of the third value. + /// The type of the fourth value. + /// The type of the fifth value. + /// The first value. + /// The second value. + /// The third value. + /// The fourth value. + /// The fifth value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) => + (value1, value2, value3, value4, value5).GetHashCode(); + + /// Combines six values into a hash code. + /// The type of the first value. + /// The type of the second value. + /// The type of the third value. + /// The type of the fourth value. + /// The type of the fifth value. + /// The type of the sixth value. + /// The first value. + /// The second value. + /// The third value. + /// The fourth value. + /// The fifth value. + /// The sixth value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) => + (value1, value2, value3, value4, value5, value6).GetHashCode(); + + /// Combines seven values into a hash code. + /// The type of the first value. + /// The type of the second value. + /// The type of the third value. + /// The type of the fourth value. + /// The type of the fifth value. + /// The type of the sixth value. + /// The type of the seventh value. + /// The first value. + /// The second value. + /// The third value. + /// The fourth value. + /// The fifth value. + /// The sixth value. + /// The seventh value. + /// The combined hash code. + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) => + (value1, value2, value3, value4, value5, value6, value7).GetHashCode(); + + /// Combines eight values into a hash code. + /// The type of the first value. + /// The type of the second value. + /// The type of the third value. + /// The type of the fourth value. + /// The type of the fifth value. + /// The type of the sixth value. + /// The type of the seventh value. + /// The type of the eighth value. + /// The first value. + /// The second value. + /// The third value. + /// The fourth value. + /// The fifth value. + /// The sixth value. + /// The seventh value. + /// The eighth value. + /// The combined hash code. + [SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "Mirrors the System.HashCode.Combine 8-arity overload.")] + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) => + (value1, value2, value3, value4, value5, value6, value7, value8).GetHashCode(); +} + +#endif diff --git a/src/ReactiveUI/Polyfills/Index.cs b/src/ReactiveUI/Polyfills/Index.cs new file mode 100644 index 0000000000..c7df751d54 --- /dev/null +++ b/src/ReactiveUI/Polyfills/Index.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +#if !NET + +namespace System; + +/// +/// Represents a type that can be used to index a collection either from the start or the end. +/// Polyfill for the framework targets that predate System.Index, enabling the ^ index operator. +/// +[ExcludeFromCodeCoverage] +internal readonly struct Index : IEquatable +{ + /// The encoded value; non-negative is measured from the start, the bitwise complement is measured from the end. + private readonly int _value; + + /// + /// Initializes a new instance of the struct. + /// + /// The index value. Must be zero or positive. + /// Indicates whether the index is counted from the start or the end. + public Index(int value, bool fromEnd = false) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), "Non-negative number required."); + } + + _value = fromEnd ? ~value : value; + } + + /// Gets an that points at the first element of a collection. + public static Index Start => new(0); + + /// Gets an that points beyond the last element of a collection. + public static Index End => new(0, true); + + /// Gets the index value (without the from-end flag). + public int Value => _value < 0 ? ~_value : _value; + + /// Gets a value indicating whether the index is counted from the end of a collection. + public bool IsFromEnd => _value < 0; + + /// Determines whether two indexes are equal. + /// The first index. + /// The second index. + /// if the indexes are equal; otherwise . + public static bool operator ==(Index left, Index right) => left.Equals(right); + + /// Determines whether two indexes are not equal. + /// The first index. + /// The second index. + /// if the indexes are not equal; otherwise . + public static bool operator !=(Index left, Index right) => !left.Equals(right); + + /// Creates an measured from the start of a collection. + /// The zero-based index from the start. + /// The created . + public static Index FromStart(int value) => new(value); + + /// Creates an measured from the end of a collection. + /// The index from the end (1 refers to the last element). + /// The created . + public static Index FromEnd(int value) => new(value, true); + + /// Calculates the offset from the start of a collection of the supplied length. + /// The length of the collection. + /// The offset from the start of the collection. + public int GetOffset(int length) + { + var offset = _value; + if (IsFromEnd) + { + offset += length + 1; + } + + return offset; + } + + /// + public bool Equals(Index other) => _value == other._value; + + /// + public override bool Equals(object? value) => value is Index other && _value == other._value; + + /// + public override int GetHashCode() => _value; +} + +#endif diff --git a/src/ReactiveUI/Polyfills/IsExternalInit.cs b/src/ReactiveUI/Polyfills/IsExternalInit.cs index aad8d6f2fb..438ef9c297 100644 --- a/src/ReactiveUI/Polyfills/IsExternalInit.cs +++ b/src/ReactiveUI/Polyfills/IsExternalInit.cs @@ -1,14 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NET - using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; namespace System.Runtime.CompilerServices; diff --git a/src/ReactiveUI/Polyfills/MaybeNullWhenAttribute.cs b/src/ReactiveUI/Polyfills/MaybeNullWhenAttribute.cs index d7d18bb18e..f903460aaf 100644 --- a/src/ReactiveUI/Polyfills/MaybeNullWhenAttribute.cs +++ b/src/ReactiveUI/Polyfills/MaybeNullWhenAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER - using System.Diagnostics; namespace System.Diagnostics.CodeAnalysis; @@ -38,7 +35,6 @@ internal sealed class MaybeNullWhenAttribute : Attribute } #else -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; [assembly: TypeForwardedTo(typeof(MaybeNullWhenAttribute))] diff --git a/src/ReactiveUI/Polyfills/MemberNotNullAttribute.cs b/src/ReactiveUI/Polyfills/MemberNotNullAttribute.cs index 6f5e5c2d99..b7531b6aa9 100644 --- a/src/ReactiveUI/Polyfills/MemberNotNullAttribute.cs +++ b/src/ReactiveUI/Polyfills/MemberNotNullAttribute.cs @@ -1,10 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. #if !NET - using System.Diagnostics; using Targets = System.AttributeTargets; @@ -20,6 +19,10 @@ Targets.Property, Inherited = false, AllowMultiple = true)] +[SuppressMessage( + "Design", + "CA1019:Define accessors for attribute arguments", + Justification = "Faithful BCL polyfill.")] internal sealed class MemberNotNullAttribute : Attribute { @@ -46,5 +49,5 @@ public MemberNotNullAttribute(params string[] members) => #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.MemberNotNullAttribute))] +[assembly: TypeForwardedTo(typeof(MemberNotNullAttribute))] #endif diff --git a/src/ReactiveUI/Polyfills/NotNullAttribute.cs b/src/ReactiveUI/Polyfills/NotNullAttribute.cs index 6400c56764..d96424c790 100644 --- a/src/ReactiveUI/Polyfills/NotNullAttribute.cs +++ b/src/ReactiveUI/Polyfills/NotNullAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER - namespace System.Diagnostics.CodeAnalysis; /// diff --git a/src/ReactiveUI/Polyfills/NotNullWhenAttribute.cs b/src/ReactiveUI/Polyfills/NotNullWhenAttribute.cs index f5292b4e26..838e1a5470 100644 --- a/src/ReactiveUI/Polyfills/NotNullWhenAttribute.cs +++ b/src/ReactiveUI/Polyfills/NotNullWhenAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from SimonCropp/Polyfill -// https://github.com/SimonCropp/Polyfill #if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER - namespace System.Diagnostics.CodeAnalysis; /// @@ -22,6 +19,7 @@ internal sealed class NotNullWhenAttribute : /// /// Initializes a new instance of the class. /// + /// The return value condition. If the method returns this value, the associated parameter will not be . public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; @@ -35,5 +33,5 @@ public NotNullWhenAttribute(bool returnValue) => #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.NotNullWhenAttribute))] +[assembly: TypeForwardedTo(typeof(NotNullWhenAttribute))] #endif diff --git a/src/ReactiveUI/Polyfills/Range.cs b/src/ReactiveUI/Polyfills/Range.cs new file mode 100644 index 0000000000..c2b2a1f0df --- /dev/null +++ b/src/ReactiveUI/Polyfills/Range.cs @@ -0,0 +1,85 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +#if !NET + +namespace System; + +/// +/// Represents a range with a start and an end index. +/// Polyfill for the framework targets that predate System.Range, enabling the .. range operator. +/// +[ExcludeFromCodeCoverage] +internal readonly struct Range : IEquatable +{ + /// + /// Initializes a new instance of the struct. + /// + /// The inclusive start index of the range. + /// The exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// Gets a that covers the entire collection. + public static Range All => new(Index.Start, Index.End); + + /// Gets the inclusive start index of the range. + public Index Start { get; } + + /// Gets the exclusive end index of the range. + public Index End { get; } + + /// Determines whether two ranges are equal. + /// The first range. + /// The second range. + /// if the ranges are equal; otherwise . + public static bool operator ==(Range left, Range right) => left.Equals(right); + + /// Determines whether two ranges are not equal. + /// The first range. + /// The second range. + /// if the ranges are not equal; otherwise . + public static bool operator !=(Range left, Range right) => !left.Equals(right); + + /// Creates a range that starts at the supplied index and runs to the end of the collection. + /// The inclusive start index. + /// The created . + public static Range StartAt(Index start) => new(start, Index.End); + + /// Creates a range that starts at the beginning of the collection and ends at the supplied index. + /// The exclusive end index. + /// The created . + public static Range EndAt(Index end) => new(Index.Start, end); + + /// Calculates the start offset and length of the range for a collection of the supplied length. + /// The length of the collection. + /// A tuple containing the start offset and the length of the range. + public (int Offset, int Length) GetOffsetAndLength(int length) + { + var start = Start.GetOffset(length); + var end = End.GetOffset(length); + + if ((uint)end > (uint)length || (uint)start > (uint)end) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + return (start, end - start); + } + + /// + public bool Equals(Range other) => Start.Equals(other.Start) && End.Equals(other.End); + + /// + public override bool Equals(object? value) => value is Range other && Equals(other); + + /// + public override int GetHashCode() => Start.GetHashCode() ^ End.GetHashCode(); +} + +#endif diff --git a/src/ReactiveUI/Polyfills/RequiresDynamicCodeAttribute.cs b/src/ReactiveUI/Polyfills/RequiresDynamicCodeAttribute.cs index e576234453..899d258c53 100644 --- a/src/ReactiveUI/Polyfills/RequiresDynamicCodeAttribute.cs +++ b/src/ReactiveUI/Polyfills/RequiresDynamicCodeAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from Simon Cropp's Polyfill library -// https://github.com/SimonCropp/Polyfill #if !NET7_0_OR_GREATER - #nullable enable using Targets = System.AttributeTargets; @@ -31,6 +28,7 @@ internal sealed class RequiresDynamicCodeAttribute : /// Initializes a new instance of the class /// with the specified message. /// + /// A message that contains information about the usage of dynamic code. public RequiresDynamicCodeAttribute(string message) => Message = message; @@ -53,5 +51,5 @@ public RequiresDynamicCodeAttribute(string message) => #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute))] +[assembly: TypeForwardedTo(typeof(RequiresDynamicCodeAttribute))] #endif diff --git a/src/ReactiveUI/Polyfills/RequiresUnreferencedCodeAttribute.cs b/src/ReactiveUI/Polyfills/RequiresUnreferencedCodeAttribute.cs index 481e7d030b..715059d199 100644 --- a/src/ReactiveUI/Polyfills/RequiresUnreferencedCodeAttribute.cs +++ b/src/ReactiveUI/Polyfills/RequiresUnreferencedCodeAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from Simon Cropp's Polyfill library -// https://github.com/SimonCropp/Polyfill #if !NET - namespace System.Diagnostics.CodeAnalysis; /// @@ -48,5 +45,5 @@ public RequiresUnreferencedCodeAttribute(string message) => #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute))] +[assembly: TypeForwardedTo(typeof(RequiresUnreferencedCodeAttribute))] #endif diff --git a/src/ReactiveUI/Polyfills/UnconditionalSuppressMessageAttribute.cs b/src/ReactiveUI/Polyfills/UnconditionalSuppressMessageAttribute.cs index 143bbe2309..f3ef841bc5 100644 --- a/src/ReactiveUI/Polyfills/UnconditionalSuppressMessageAttribute.cs +++ b/src/ReactiveUI/Polyfills/UnconditionalSuppressMessageAttribute.cs @@ -1,12 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -// Polyfill implementation adapted from Simon Cropp's Polyfill library -// https://github.com/SimonCropp/Polyfill #if !NET - namespace System.Diagnostics.CodeAnalysis; /// @@ -70,5 +67,5 @@ public UnconditionalSuppressMessageAttribute(string category, string checkId) #else using System.Runtime.CompilerServices; -[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute))] +[assembly: TypeForwardedTo(typeof(UnconditionalSuppressMessageAttribute))] #endif diff --git a/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs index d0a5a56a7b..ccbad53c53 100644 --- a/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/CombinedReactiveCommand.cs @@ -1,8 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -32,10 +36,42 @@ namespace ReactiveUI; /// public class CombinedReactiveCommand : ReactiveCommandBase> { + /// The inner command that executes all child commands and aggregates their results. private readonly ReactiveCommand> _innerCommand; - private readonly ScheduledSubject _exceptions; - private readonly IDisposable _exceptionsSubscription; - private readonly IScheduler _outputScheduler; + + /// The merged exception stream of the inner command and every child command. + private readonly IObservable _thrownExceptions; + + /// Subscription that drives the CanExecuteChanged event. + private readonly IDisposable _canExecuteSubscription; + + /// + /// Initializes a new instance of the class using the default output scheduler. + /// + /// The child commands which will be executed. + /// An observable indicating when the command can be executed. + /// Fires when required arguments are null. + /// Fires if the child commands container is empty. + protected internal CombinedReactiveCommand( + IEnumerable> childCommands, + IObservable? canExecute) + : this(childCommands, canExecute, null) + { + } + + /// + /// Initializes a new instance of the class using the default can-execute behavior. + /// + /// The child commands which will be executed. + /// The scheduler where to dispatch the output from the command. + /// Fires when required arguments are null. + /// Fires if the child commands container is empty. + protected internal CombinedReactiveCommand( + IEnumerable> childCommands, + IScheduler? outputScheduler) + : this(childCommands, null, outputScheduler) + { + } /// /// Initializes a new instance of the class. @@ -48,56 +84,55 @@ public class CombinedReactiveCommand : ReactiveCommandBase> childCommands, IObservable? canExecute, - IScheduler? outputScheduler = null) + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(childCommands); - _outputScheduler = outputScheduler ?? RxSchedulers.MainThreadScheduler; + var localOutputScheduler = outputScheduler ?? RxSchedulers.MainThreadScheduler; - var childCommandsArray = childCommands.ToArray(); + ReactiveCommandBase[] childCommandsArray = [.. childCommands]; if (childCommandsArray.Length == 0) { throw new ArgumentException("No child commands provided.", nameof(childCommands)); } - _exceptions = new ScheduledSubject(_outputScheduler, RxState.DefaultExceptionHandler); - - var canChildrenExecute = childCommandsArray.Select(x => x.CanExecute) - .CombineLatest() - .Select(x => x.All(y => y)); - var combinedCanExecute = (canExecute ?? Observables.True) - .Catch(ex => - { - _exceptions.OnNext(ex); - return Observables.False; - }) - .StartWith(false) - .CombineLatest(canChildrenExecute, (ce, cce) => ce && cce) - .DistinctUntilChanged() - .Replay(1) - .RefCount(); - - _exceptionsSubscription = childCommandsArray.Select(x => x.ThrownExceptions) - .Merge() - .Subscribe(ex => _exceptions.OnNext(ex)); - - _innerCommand = new ReactiveCommand>( - param => - childCommandsArray - .Select(x => x.Execute(param)) - .CombineLatest(), - combinedCanExecute, - _outputScheduler); - - // we already handle exceptions on individual child commands above, but the same exception - // will tick through innerCommand. Therefore, we need to ensure we ignore it or the default - // handler will execute and the process will be torn down - _innerCommand - .ThrownExceptions - .Subscribe(); - - CanExecute.Subscribe(OnCanExecuteChanged); + var childCanExecute = new IObservable[childCommandsArray.Length]; + for (var i = 0; i < childCommandsArray.Length; i++) + { + childCanExecute[i] = childCommandsArray[i].CanExecute; + } + + // all-true of [parent, child0..childN] (the parent gate plus every child's CanExecute). + var canExecuteSources = new IObservable[childCommandsArray.Length + 1]; + canExecuteSources[0] = canExecute ?? SingleValueObservable.True; + Array.Copy(childCanExecute, 0, canExecuteSources, 1, childCanExecute.Length); + var combinedCanExecute = new AllTrueCanExecuteObservable(canExecuteSources); + + _innerCommand = new( + param => + { + var executions = new IObservable[childCommandsArray.Length]; + for (var i = 0; i < childCommandsArray.Length; i++) + { + executions[i] = childCommandsArray[i].Execute(param); + } + + return new CombinedResultsObservable(executions); + }, + combinedCanExecute, + localOutputScheduler); + + var exceptionSources = new IObservable[childCommandsArray.Length + 1]; + exceptionSources[0] = _innerCommand.ThrownExceptions; + for (var i = 0; i < childCommandsArray.Length; i++) + { + exceptionSources[i + 1] = childCommandsArray[i].ThrownExceptions; + } + + _thrownExceptions = new MergedExceptionsObservable(exceptionSources); + + _canExecuteSubscription = CanExecute.Subscribe(new DelegateObserver(OnCanExecuteChanged)); } /// @@ -107,7 +142,7 @@ protected internal CombinedReactiveCommand( public override IObservable IsExecuting => _innerCommand.IsExecuting; /// - public override IObservable ThrownExceptions => _exceptions; + public override IObservable ThrownExceptions => _thrownExceptions; /// public override IDisposable Subscribe(IObserver> observer) => _innerCommand.Subscribe(observer); @@ -121,11 +156,446 @@ protected internal CombinedReactiveCommand( /// protected override void Dispose(bool disposing) { - if (disposing) + if (!disposing) + { + return; + } + + _canExecuteSubscription.Dispose(); + _innerCommand.Dispose(); + } + + /// + /// Combines the latest CanExecute of the parent gate and every child command, emitting true only when all of + /// them are currently true. Specialised to the combined command; no generic combine-latest operator. + /// + /// The parent gate followed by each child command's CanExecute. + private sealed class AllTrueCanExecuteObservable(IObservable[] sources) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, sources); + } + + /// Tracks the latest value of each source and emits their all-true when every source has reported. + private sealed class Sink : IDisposable + { + /// Guards the latest values and the arrival/completion counters. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving the combined value. + private readonly IObserver _downstream; + + /// The latest value reported by each source. + private readonly bool[] _latest; + + /// Whether each source has reported at least one value. + private readonly bool[] _has; + + /// The subscriptions to each source. + private readonly IDisposable?[] _subscriptions; + + /// The number of sources that have reported at least one value. + private int _haveCount; + + /// The number of sources that have completed. + private int _doneCount; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Initializes a new instance of the class and subscribes to every source. + /// The observer receiving the combined value. + /// The sources to combine. + public Sink(IObserver downstream, IObservable[] sources) + { + _downstream = downstream; + _latest = new bool[sources.Length]; + _has = new bool[sources.Length]; + _subscriptions = new IDisposable?[sources.Length]; + for (var i = 0; i < sources.Length; i++) + { + _subscriptions[i] = sources[i].Subscribe(new Element(this, i)); + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Records a source value and emits the all-true once every source has reported. + /// The source index. + /// The reported value. + private void OnNextAt(int index, bool value) + { + bool all; + lock (_gate) + { + if (_stopped) + { + return; + } + + if (!_has[index]) + { + _has[index] = true; + _haveCount++; + } + + _latest[index] = value; + if (_haveCount < _latest.Length) + { + return; + } + + all = true; + for (var j = 0; j < _latest.Length; j++) + { + if (!_latest[j]) + { + all = false; + break; + } + } + } + + _downstream.OnNext(all); + } + + /// Forwards an error from any source. + /// The error to forward. + private void OnErrorAt(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _downstream.OnError(error); + } + + /// Completes the downstream once every source has completed. + private void OnCompletedAt() + { + lock (_gate) + { + if (_stopped || ++_doneCount < _latest.Length) + { + return; + } + + _stopped = true; + } + + _downstream.OnCompleted(); + } + + /// Routes one source's notifications to the parent sink, tagged with its index. + /// The owning sink. + /// The source index. + private sealed class Element(Sink parent, int index) : IObserver + { + /// + public void OnNext(bool value) => parent.OnNextAt(index, value); + + /// + public void OnError(Exception error) => parent.OnErrorAt(error); + + /// + public void OnCompleted() => parent.OnCompletedAt(); + } + } + } + + /// + /// Combines the latest result of every child execution into a single list, emitting once every child has produced a + /// value. Specialised to the combined command; no generic combine-latest operator. + /// + /// The in-flight execution of each child command. + private sealed class CombinedResultsObservable(IObservable[] sources) : IObservable> + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, sources); + } + + /// Tracks the latest result of each child and emits their list once every child has reported. + private sealed class Sink : IDisposable { - _innerCommand.Dispose(); - _exceptions.Dispose(); - _exceptionsSubscription.Dispose(); + /// Guards the latest results and the arrival/completion counters. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving the combined list. + private readonly IObserver> _downstream; + + /// The latest result reported by each child. + private readonly TResult[] _latest; + + /// Whether each child has reported at least one result. + private readonly bool[] _has; + + /// The subscriptions to each child execution. + private readonly IDisposable?[] _subscriptions; + + /// The number of children that have reported at least one result. + private int _haveCount; + + /// The number of children that have completed. + private int _doneCount; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Initializes a new instance of the class and subscribes to every child execution. + /// The observer receiving the combined list. + /// The child executions to combine. + public Sink(IObserver> downstream, IObservable[] sources) + { + _downstream = downstream; + _latest = new TResult[sources.Length]; + _has = new bool[sources.Length]; + _subscriptions = new IDisposable?[sources.Length]; + for (var i = 0; i < sources.Length; i++) + { + _subscriptions[i] = sources[i].Subscribe(new Element(this, i)); + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Records a child result and emits the combined list once every child has reported. + /// The child index. + /// The reported result. + private void OnNextAt(int index, TResult value) + { + IList list; + lock (_gate) + { + if (_stopped) + { + return; + } + + if (!_has[index]) + { + _has[index] = true; + _haveCount++; + } + + _latest[index] = value; + if (_haveCount < _latest.Length) + { + return; + } + + list = [.. _latest]; + } + + _downstream.OnNext(list); + } + + /// Forwards an error from any child execution. + /// The error to forward. + private void OnErrorAt(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _downstream.OnError(error); + } + + /// Completes the downstream once every child execution has completed. + private void OnCompletedAt() + { + lock (_gate) + { + if (_stopped || ++_doneCount < _latest.Length) + { + return; + } + + _stopped = true; + } + + _downstream.OnCompleted(); + } + + /// Routes one child execution's notifications to the parent sink, tagged with its index. + /// The owning sink. + /// The child index. + private sealed class Element(Sink parent, int index) : IObserver + { + /// + public void OnNext(TResult value) => parent.OnNextAt(index, value); + + /// + public void OnError(Exception error) => parent.OnErrorAt(error); + + /// + public void OnCompleted() => parent.OnCompletedAt(); + } + } + } + + /// + /// Forwards the thrown exceptions of the inner command and every child command into one stream. Specialised to the + /// combined command; no generic merge operator. + /// + /// The inner and child exception streams. + private sealed class MergedExceptionsObservable(IObservable[] sources) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, sources); + } + + /// Forwards every source value and completes once every source has completed. + private sealed class Sink : IDisposable + { + /// Guards downstream delivery and the completion counter. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving the merged exceptions. + private readonly IObserver _downstream; + + /// The subscriptions to each source. + private readonly IDisposable?[] _subscriptions; + + /// The number of sources. + private readonly int _count; + + /// The number of sources that have completed. + private int _doneCount; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Initializes a new instance of the class and subscribes to every source. + /// The observer receiving the merged exceptions. + /// The exception streams to merge. + public Sink(IObserver downstream, IObservable[] sources) + { + _downstream = downstream; + _count = sources.Length; + _subscriptions = new IDisposable?[sources.Length]; + for (var i = 0; i < sources.Length; i++) + { + _subscriptions[i] = sources[i].Subscribe(new Element(this)); + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Forwards one source value to the downstream. + /// The exception to forward. + private void OnNextAt(Exception value) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _downstream.OnNext(value); + } + } + + /// Forwards an error from any source. + /// The error to forward. + private void OnErrorAt(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _downstream.OnError(error); + } + + /// Completes the downstream once every source has completed. + private void OnCompletedAt() + { + lock (_gate) + { + if (_stopped || ++_doneCount < _count) + { + return; + } + + _stopped = true; + } + + _downstream.OnCompleted(); + } + + /// Routes one source's notifications to the parent sink. + /// The owning sink. + private sealed class Element(Sink parent) : IObserver + { + /// + public void OnNext(Exception value) => parent.OnNextAt(value); + + /// + public void OnError(Exception error) => parent.OnErrorAt(error); + + /// + public void OnCompleted() => parent.OnCompletedAt(); + } } } } diff --git a/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs index b298fb9952..4fe4846453 100644 --- a/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/IReactiveCommand.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs index 153519e5e9..4d765a3361 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs @@ -1,8 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -62,6 +67,39 @@ namespace ReactiveUI; /// public static class ReactiveCommand { + /// + /// Creates a parameterless reactive command with synchronous execution logic. + /// + /// The action to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create(Action execute) => + Create(execute, null, null); + + /// + /// Creates a parameterless reactive command with synchronous execution logic. + /// + /// The action to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Action execute, + IObservable? canExecute) => + Create(execute, canExecute, null); + + /// + /// Creates a parameterless reactive command with synchronous execution logic. + /// + /// The action to execute whenever the command is executed. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Action execute, + IScheduler? outputScheduler) => + Create(execute, null, outputScheduler); + /// /// Creates a parameterless with synchronous execution logic. /// @@ -74,50 +112,190 @@ public static class ReactiveCommand /// execute. public static ReactiveCommand Create( Action execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return new ReactiveCommand( - _ => Observable.Create( - observer => - { - execute(); - observer.OnNext(Unit.Default); - observer.OnCompleted(); - return Disposable.Empty; - }), - canExecute, - outputScheduler); + return new( + _ => new SyncExecuteObservable(() => + { + execute(); + return Unit.Default; + }), + canExecute, + outputScheduler); } /// - /// Creates a parameterless with asynchronous execution logic. + /// Creates a parameterless reactive command with synchronous execution logic that returns a value of type TResult. + /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create(Func execute) => + Create(execute, null, null); + + /// + /// Creates a parameterless reactive command with synchronous execution logic that returns a value of type TResult. + /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Func execute, + IObservable? canExecute) => + Create(execute, canExecute, null); + + /// + /// Creates a parameterless reactive command with synchronous execution logic that returns a value of type TResult. + /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Func execute, + IScheduler? outputScheduler) => + Create(execute, null, outputScheduler); + + /// + /// Creates a parameterless with synchronous execution logic that returns a value + /// of type . /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// + /// The ReactiveCommand instance. + /// + /// execute. + public static ReactiveCommand Create( + Func execute, + IObservable? canExecute, + IScheduler? outputScheduler) + { + ArgumentExceptionHelper.ThrowIfNull(execute); + + return new( + _ => new SyncExecuteObservable(execute), + canExecute, + outputScheduler); + } + + /// + /// Creates a reactive command with synchronous execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The action to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create(Action execute) => + Create(execute, null, null); + + /// + /// Creates a reactive command with synchronous execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The action to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Action execute, + IObservable? canExecute) => + Create(execute, canExecute, null); + + /// + /// Creates a reactive command with synchronous execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The action to execute whenever the command is executed. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Action execute, + IScheduler? outputScheduler) => + Create(execute, null, outputScheduler); + + /// + /// Creates a with synchronous execution logic that takes a parameter of type . + /// + /// The type of the parameter passed through to command execution. /// The action to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. - /// The background scheduler. /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. /// /// The ReactiveCommand instance. /// /// execute. - public static ReactiveCommand CreateRunInBackground( - Action execute, - IObservable? canExecute = null, - IScheduler? backgroundScheduler = null, - IScheduler? outputScheduler = null) + public static ReactiveCommand Create( + Action execute, + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservable(() => Observable.Start(execute, backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); + return new( + param => new SyncExecuteObservable(() => + { + execute(param); + return Unit.Default; + }), + canExecute, + outputScheduler); } /// - /// Creates a parameterless with synchronous execution logic that returns a value - /// of type . + /// Creates a reactive command with synchronous execution logic that takes a parameter of type TParam and returns a value of type TResult. /// + /// The type of the parameter passed through to command execution. + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create(Func execute) => + Create(execute, null, null); + + /// + /// Creates a reactive command with synchronous execution logic that takes a parameter of type TParam and returns a value of type TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Func execute, + IObservable? canExecute) => + Create(execute, canExecute, null); + + /// + /// Creates a reactive command with synchronous execution logic that takes a parameter of type TParam and returns a value of type TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand Create( + Func execute, + IScheduler? outputScheduler) => + Create(execute, null, outputScheduler); + + /// + /// Creates a with synchronous execution logic that takes a parameter of type + /// and returns a value of type . + /// + /// The type of the parameter passed through to command execution. /// The type of value returned by command executions. /// The function to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. @@ -126,25 +304,152 @@ public static ReactiveCommand CreateRunInBackground( /// The ReactiveCommand instance. /// /// execute. - public static ReactiveCommand Create( - Func execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + public static ReactiveCommand Create( + Func execute, + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return new ReactiveCommand( - _ => Observable.Create( - observer => + return new( + param => new SyncExecuteObservable(() => execute(param)), + canExecute, + outputScheduler); + } + + /// + /// Creates a parameterless reactive command with asynchronous background execution logic. + /// + /// The action to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground(Action execute) => + CreateRunInBackground(execute, null, null, null); + + /// + /// Creates a parameterless reactive command with asynchronous background execution logic. + /// + /// The action to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Action execute, + IObservable? canExecute) => + CreateRunInBackground(execute, canExecute, null, null); + + /// + /// Creates a parameterless reactive command with asynchronous background execution logic. + /// + /// The action to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The background scheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Action execute, + IObservable? canExecute, + IScheduler? backgroundScheduler) => + CreateRunInBackground(execute, canExecute, backgroundScheduler, null); + + /// + /// Creates a parameterless reactive command with asynchronous background execution logic. + /// + /// The action to execute whenever the command is executed. + /// The background scheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Action execute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) => + CreateRunInBackground(execute, null, backgroundScheduler, outputScheduler); + + /// + /// Creates a parameterless with asynchronous execution logic. + /// + /// The action to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The background scheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// + /// The ReactiveCommand instance. + /// + /// execute. + public static ReactiveCommand CreateRunInBackground( + Action execute, + IObservable? canExecute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) + { + ArgumentExceptionHelper.ThrowIfNull(execute); + + return CreateFromObservable( + () => new StartObservable( + () => { - observer.OnNext(execute()); - observer.OnCompleted(); - return Disposable.Empty; - }), - canExecute, - outputScheduler); + execute(); + return Unit.Default; + }, + backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), + canExecute, + outputScheduler); } + /// + /// Creates a parameterless reactive command with asynchronous background execution logic that returns a value of type TResult. + /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground(Func execute) => + CreateRunInBackground(execute, null, null, null); + + /// + /// Creates a parameterless reactive command with asynchronous background execution logic that returns a value of type TResult. + /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Func execute, + IObservable? canExecute) => + CreateRunInBackground(execute, canExecute, null, null); + + /// + /// Creates a parameterless reactive command with asynchronous background execution logic that returns a value of type TResult. + /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The background scheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Func execute, + IObservable? canExecute, + IScheduler? backgroundScheduler) => + CreateRunInBackground(execute, canExecute, backgroundScheduler, null); + + /// + /// Creates a parameterless reactive command with asynchronous background execution logic that returns a value of type TResult. + /// + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// The background scheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Func execute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) => + CreateRunInBackground(execute, null, backgroundScheduler, outputScheduler); + /// /// Creates a parameterless with asynchronous execution logic that returns a value /// of type . @@ -160,45 +465,70 @@ public static ReactiveCommand Create( /// execute. public static ReactiveCommand CreateRunInBackground( Func execute, - IObservable? canExecute = null, - IScheduler? backgroundScheduler = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservable(() => Observable.Start(execute, backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); + return CreateFromObservable( + () => new StartObservable(execute, backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), + canExecute, + outputScheduler); } /// - /// Creates a with synchronous execution logic that takes a parameter of type . + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The action to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground(Action execute) => + CreateRunInBackground(execute, null, null, null); + + /// + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam. /// /// The type of the parameter passed through to command execution. /// The action to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. - /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. - /// - /// The ReactiveCommand instance. - /// + /// The ReactiveCommand instance. /// execute. - public static ReactiveCommand Create( + public static ReactiveCommand CreateRunInBackground( Action execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) - { - ArgumentExceptionHelper.ThrowIfNull(execute); + IObservable? canExecute) => + CreateRunInBackground(execute, canExecute, null, null); - return new ReactiveCommand( - param => Observable.Create( - observer => - { - execute(param); - observer.OnNext(Unit.Default); - observer.OnCompleted(); - return Disposable.Empty; - }), - canExecute, - outputScheduler); - } + /// + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The action to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The background scheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Action execute, + IObservable? canExecute, + IScheduler? backgroundScheduler) => + CreateRunInBackground(execute, canExecute, backgroundScheduler, null); + + /// + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The action to execute whenever the command is executed. + /// The background scheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Action execute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) => + CreateRunInBackground(execute, null, backgroundScheduler, outputScheduler); /// /// Creates a with asynchronous execution logic that takes a parameter of type . @@ -214,46 +544,81 @@ public static ReactiveCommand Create( /// execute. public static ReactiveCommand CreateRunInBackground( Action execute, - IObservable? canExecute = null, - IScheduler? backgroundScheduler = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservable(p => Observable.Start(() => execute(p), backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); + return CreateFromObservable( + p => + new StartObservable( + () => + { + execute(p); + return Unit.Default; + }, + backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), + canExecute, + outputScheduler); } /// - /// Creates a with synchronous execution logic that takes a parameter of type - /// and returns a value of type . + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam and returns a value of type TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground(Func execute) => + CreateRunInBackground(execute, null, null, null); + + /// + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam and returns a value of type TResult. /// /// The type of the parameter passed through to command execution. /// The type of value returned by command executions. /// The function to execute whenever the command is executed. /// An optional observable that dictates the availability of the command for execution. - /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. - /// - /// The ReactiveCommand instance. - /// + /// The ReactiveCommand instance. /// execute. - public static ReactiveCommand Create( + public static ReactiveCommand CreateRunInBackground( Func execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) - { - ArgumentExceptionHelper.ThrowIfNull(execute); + IObservable? canExecute) => + CreateRunInBackground(execute, canExecute, null, null); - return new ReactiveCommand( - param => Observable.Create( - observer => - { - observer.OnNext(execute(param)); - observer.OnCompleted(); - return Disposable.Empty; - }), - canExecute, - outputScheduler); - } + /// + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam and returns a value of type TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// An optional observable that dictates the availability of the command for execution. + /// The background scheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Func execute, + IObservable? canExecute, + IScheduler? backgroundScheduler) => + CreateRunInBackground(execute, canExecute, backgroundScheduler, null); + + /// + /// Creates a reactive command with asynchronous background execution logic that takes a parameter of type TParam and returns a value of type TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of value returned by command executions. + /// The function to execute whenever the command is executed. + /// The background scheduler. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateRunInBackground( + Func execute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) => + CreateRunInBackground(execute, null, backgroundScheduler, outputScheduler); /// /// Creates a with asynchronous execution logic that takes a parameter of type @@ -271,15 +636,55 @@ public static ReactiveCommand Create( /// execute. public static ReactiveCommand CreateRunInBackground( Func execute, - IObservable? canExecute = null, - IScheduler? backgroundScheduler = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? backgroundScheduler, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservable(p => Observable.Start(() => execute(p), backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), canExecute, outputScheduler); + return CreateFromObservable( + p => new StartObservable(() => execute(p), backgroundScheduler ?? RxSchedulers.TaskpoolScheduler), + canExecute, + outputScheduler); } + /// + /// Creates a combined reactive command that composes all the provided child commands. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// The child commands that the combined command will compose. + /// The CombinedReactiveCommand instance. + public static CombinedReactiveCommand CreateCombined( + IEnumerable> childCommands) => + CreateCombined(childCommands, null, null); + + /// + /// Creates a combined reactive command that composes all the provided child commands. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// The child commands that the combined command will compose. + /// An optional observable that dictates the availability of the command for execution. + /// The CombinedReactiveCommand instance. + public static CombinedReactiveCommand CreateCombined( + IEnumerable> childCommands, + IObservable? canExecute) => + CreateCombined(childCommands, canExecute, null); + + /// + /// Creates a combined reactive command that composes all the provided child commands. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// The child commands that the combined command will compose. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The CombinedReactiveCommand instance. + public static CombinedReactiveCommand CreateCombined( + IEnumerable> childCommands, + IScheduler? outputScheduler) => + CreateCombined(childCommands, null, outputScheduler); + /// /// Creates a that composes all the provided child commands. /// @@ -304,14 +709,49 @@ public static ReactiveCommand CreateRunInBackground public static CombinedReactiveCommand CreateCombined( IEnumerable> childCommands, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(childCommands); - return new CombinedReactiveCommand(childCommands, canExecute, outputScheduler); + return new(childCommands, canExecute, outputScheduler); } + /// + /// Creates a parameterless reactive command with asynchronous observable execution logic. + /// + /// The type of the command's result. + /// Provides an observable representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromObservable( + Func> execute) => + CreateFromObservable(execute, null, null); + + /// + /// Creates a parameterless reactive command with asynchronous observable execution logic. + /// + /// The type of the command's result. + /// Provides an observable representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromObservable( + Func> execute, + IObservable? canExecute) => + CreateFromObservable(execute, canExecute, null); + + /// + /// Creates a parameterless reactive command with asynchronous observable execution logic. + /// + /// The type of the command's result. + /// Provides an observable representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromObservable( + Func> execute, + IScheduler? outputScheduler) => + CreateFromObservable(execute, null, outputScheduler); + /// /// Creates a parameterless with asynchronous execution logic. /// @@ -332,17 +772,55 @@ public static CombinedReactiveCommand CreateCombined public static ReactiveCommand CreateFromObservable( Func> execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return new ReactiveCommand( - _ => execute(), - canExecute, - outputScheduler); + return new( + _ => execute(), + canExecute, + outputScheduler); } + /// + /// Creates a reactive command with asynchronous observable execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides an observable representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromObservable( + Func> execute) => + CreateFromObservable(execute, null, null); + + /// + /// Creates a reactive command with asynchronous observable execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides an observable representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromObservable( + Func> execute, + IObservable? canExecute) => + CreateFromObservable(execute, canExecute, null); + + /// + /// Creates a reactive command with asynchronous observable execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides an observable representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromObservable( + Func> execute, + IScheduler? outputScheduler) => + CreateFromObservable(execute, null, outputScheduler); + /// /// Creates a with asynchronous execution logic that takes a parameter of type . /// @@ -366,16 +844,51 @@ public static ReactiveCommand CreateFromObservable( /// public static ReactiveCommand CreateFromObservable( Func> execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return new ReactiveCommand( - execute, - canExecute, - outputScheduler); - } + return new( + execute, + canExecute, + outputScheduler); + } + + /// + /// Creates a parameterless reactive command with asynchronous task-based execution logic returning TResult. + /// + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a parameterless reactive command with asynchronous task-based execution logic returning TResult. + /// + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a parameterless reactive command with asynchronous task-based execution logic returning TResult. + /// + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func> execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); /// /// Creates a parameterless with asynchronous execution logic. @@ -397,14 +910,49 @@ public static ReactiveCommand CreateFromObservable public static ReactiveCommand CreateFromTask( Func> execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservable(() => execute().ToObservable(), canExecute, outputScheduler); + return CreateFromObservable(() => new TaskObservable(execute()), canExecute, outputScheduler); } + /// + /// Creates a parameterless, cancellable reactive command with asynchronous task-based execution logic returning TResult. + /// + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a parameterless, cancellable reactive command with asynchronous task-based execution logic returning TResult. + /// + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a parameterless, cancellable reactive command with asynchronous task-based execution logic returning TResult. + /// + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func> execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); + /// /// Creates a parameterless, cancellable with asynchronous execution logic. /// @@ -425,14 +973,48 @@ public static ReactiveCommand CreateFromTask( /// public static ReactiveCommand CreateFromTask( Func> execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservableCancellable(() => ObservableMixins.FromAsyncWithAllNotifications(execute), canExecute, outputScheduler); + return CreateFromObservableCancellable( + () => ObservableMixins.FromAsyncWithAllNotifications(execute), + canExecute, + outputScheduler); } + /// + /// Creates a parameterless reactive command with asynchronous task-based execution logic. + /// + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask(Func execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a parameterless reactive command with asynchronous task-based execution logic. + /// + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a parameterless reactive command with asynchronous task-based execution logic. + /// + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); + /// /// Creates a parameterless with asynchronous execution logic. /// @@ -450,14 +1032,45 @@ public static ReactiveCommand CreateFromTask( /// public static ReactiveCommand CreateFromTask( Func execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservable(() => execute().ToObservable(), canExecute, outputScheduler); + return CreateFromObservable(() => new TaskUnitObservable(execute()), canExecute, outputScheduler); } + /// + /// Creates a parameterless, cancellable reactive command with asynchronous task-based execution logic. + /// + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask(Func execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a parameterless, cancellable reactive command with asynchronous task-based execution logic. + /// + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a parameterless, cancellable reactive command with asynchronous task-based execution logic. + /// + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); + /// /// Creates a parameterless, cancellable with asynchronous execution logic. /// @@ -475,14 +1088,55 @@ public static ReactiveCommand CreateFromTask( /// public static ReactiveCommand CreateFromTask( Func execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); - return CreateFromObservableCancellable(() => ObservableMixins.FromAsyncWithAllNotifications(execute), canExecute, outputScheduler); + return CreateFromObservableCancellable( + () => ObservableMixins.FromAsyncWithAllNotifications(execute), + canExecute, + outputScheduler); } + /// + /// Creates a reactive command with asynchronous task-based execution logic that takes a parameter of type TParam and returns TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a reactive command with asynchronous task-based execution logic that takes a parameter of type TParam and returns TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a reactive command with asynchronous task-based execution logic that takes a parameter of type TParam and returns TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func> execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); + /// /// Creates a with asynchronous execution logic that takes a parameter of type . /// @@ -506,17 +1160,55 @@ public static ReactiveCommand CreateFromTask( /// public static ReactiveCommand CreateFromTask( Func> execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); return CreateFromObservable( - param => execute(param).ToObservable(), - canExecute, - outputScheduler); + param => new TaskObservable(execute(param)), + canExecute, + outputScheduler); } + /// + /// Creates a reactive command with asynchronous, cancellable task-based execution logic that takes TParam and returns TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a reactive command with asynchronous, cancellable task-based execution logic that takes TParam and returns TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func> execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a reactive command with asynchronous, cancellable task-based execution logic that takes TParam and returns TResult. + /// + /// The type of the parameter passed through to command execution. + /// The type of the command's result. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func> execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); + /// /// Creates a with asynchronous, cancellable execution logic that takes a parameter of type . /// @@ -540,17 +1232,52 @@ public static ReactiveCommand CreateFromTask( /// public static ReactiveCommand CreateFromTask( Func> execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); return CreateFromObservableCancellable( - param => ObservableMixins.FromAsyncWithAllNotifications(ct => execute(param, ct)), - canExecute, - outputScheduler); + param => ObservableMixins.FromAsyncWithAllNotifications(ct => execute(param, ct)), + canExecute, + outputScheduler); } + /// + /// Creates a reactive command with asynchronous task-based execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a reactive command with asynchronous task-based execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a reactive command with asynchronous task-based execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); + /// /// Creates a with asynchronous execution logic that takes a parameter of type . /// @@ -571,17 +1298,52 @@ public static ReactiveCommand CreateFromTask( /// public static ReactiveCommand CreateFromTask( Func execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); return CreateFromObservable( - param => execute(param).ToObservable(), - canExecute, - outputScheduler); + param => new TaskUnitObservable(execute(param)), + canExecute, + outputScheduler); } + /// + /// Creates a reactive command with asynchronous, cancellable task-based execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// Provides a Task representing the command's asynchronous execution logic. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func execute) => + CreateFromTask(execute, null, null); + + /// + /// Creates a reactive command with asynchronous, cancellable task-based execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional observable that dictates the availability of the command for execution. + /// The ReactiveCommand instance. + public static ReactiveCommand CreateFromTask( + Func execute, + IObservable? canExecute) => + CreateFromTask(execute, canExecute, null); + + /// + /// Creates a reactive command with asynchronous, cancellable task-based execution logic that takes a parameter of type TParam. + /// + /// The type of the parameter passed through to command execution. + /// Provides a Task representing the command's asynchronous execution logic. + /// An optional scheduler that is used to surface events. Defaults to RxSchedulers.MainThreadScheduler. + /// The ReactiveCommand instance. + /// execute. + public static ReactiveCommand CreateFromTask( + Func execute, + IScheduler? outputScheduler) => + CreateFromTask(execute, null, outputScheduler); + /// /// Creates a with asynchronous, cancellable execution logic that takes a parameter of type . /// @@ -602,15 +1364,15 @@ public static ReactiveCommand CreateFromTask( /// public static ReactiveCommand CreateFromTask( Func execute, - IObservable? canExecute = null, - IScheduler? outputScheduler = null) + IObservable? canExecute, + IScheduler? outputScheduler) { ArgumentExceptionHelper.ThrowIfNull(execute); return CreateFromObservableCancellable( - param => ObservableMixins.FromAsyncWithAllNotifications(ct => execute(param, ct)), - canExecute, - outputScheduler); + param => ObservableMixins.FromAsyncWithAllNotifications(ct => execute(param, ct)), + canExecute, + outputScheduler); } /// @@ -625,6 +1387,10 @@ public static ReactiveCommand CreateFromTask( /// The ReactiveCommand instance. /// /// execute. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] internal static ReactiveCommand CreateFromObservableCancellable( Func Result, Action Cancel)>> execute, IObservable? canExecute = null, @@ -632,7 +1398,7 @@ internal static ReactiveCommand CreateFromObservableCancellable { ArgumentExceptionHelper.ThrowIfNull(execute); - return new ReactiveCommand( + return new( _ => execute(), canExecute, outputScheduler); @@ -666,242 +1432,9 @@ internal static ReactiveCommand CreateFromObservableCancellable { ArgumentExceptionHelper.ThrowIfNull(execute); - return new ReactiveCommand( + return new( execute, canExecute, outputScheduler); } } - -/// -/// Encapsulates a user interaction behind a reactive interface. -/// -/// -/// -/// This class provides the bulk of the actual implementation for reactive commands. You should not create instances -/// of this class directly, but rather via the static creation methods on the non-generic -/// class. -/// -/// -/// -/// The type of parameter values passed in during command execution. -/// -/// -/// The type of the values that are the result of command execution. -/// -[SuppressMessage( - "StyleCop.CSharp.MaintainabilityRules", - "SA1402:FileMayOnlyContainASingleType", - Justification = "Same class just generic.")] -public class ReactiveCommand : ReactiveCommandBase -{ - private readonly IObservable _canExecute; - private readonly IDisposable _canExecuteSubscription; - [SuppressMessage("Design", "CA2213: Dispose member", Justification = "Internal use only")] - private readonly ScheduledSubject _exceptions; - private readonly Func Result, Action Cancel)>> _execute; - [SuppressMessage("Design", "CA2213: Dispose member", Justification = "Internal use only")] - private readonly Subject _executionInfo; - private readonly IObservable _isExecuting; - private readonly IScheduler _outputScheduler; - private readonly IObservable _results; - private readonly ISubject _synchronizedExecutionInfo; - - /// - /// Initializes a new instance of the class for work - /// that signals cancellation through a separate callback (as opposed to cancelling by - /// unsubscribing). - /// - /// The Func to perform when the command is executed. - /// A observable which has a value if the command can execute. - /// The scheduler where to send output after the main execution. - /// - /// execute. - /// - /// Thrown if any dependent parameters are null. - protected internal ReactiveCommand( - Func Result, Action Cancel)>> execute, - IObservable? canExecute, - IScheduler? outputScheduler) - { - _execute = execute ?? throw new ArgumentNullException(nameof(execute)); - _outputScheduler = outputScheduler ?? RxSchedulers.MainThreadScheduler; - _exceptions = new ScheduledSubject(_outputScheduler, RxState.DefaultExceptionHandler); - _executionInfo = new Subject(); - _synchronizedExecutionInfo = Subject.Synchronize(_executionInfo, _outputScheduler); - _isExecuting = _synchronizedExecutionInfo - .Scan( - 0, - (acc, next) => next.Demarcation switch - { - ExecutionDemarcation.Begin => acc + 1, - ExecutionDemarcation.End => acc > 0 ? acc - 1 : acc = 0, - _ => acc - }) - .Select(inFlightCount => inFlightCount > 0) - .StartWith(false) - .DistinctUntilChanged() - .Replay(1) - .RefCount(); - - _canExecute = (canExecute ?? Observables.True) - .Catch( - ex => - { - _exceptions.OnNext(ex); - return Observables.False; - }).StartWith(false) - .CombineLatest(_isExecuting, (canEx, isEx) => canEx && !isEx) - .DistinctUntilChanged() - .Replay(1) - .RefCount(); - - _results = _synchronizedExecutionInfo.Where(x => x.Demarcation == ExecutionDemarcation.Result) - .Select(x => x.Result); - - _canExecuteSubscription = _canExecute - .Subscribe(OnCanExecuteChanged); - } - - /// - /// Initializes a new instance of the class. - /// - /// The Func to perform when the command is executed. - /// A observable which has a value if the command can execute. - /// The scheduler where to send output after the main execution. - /// - /// execute. - /// - /// Thrown if any dependent parameters are null. - protected internal ReactiveCommand( - Func> execute, - IObservable? canExecute, - IScheduler? outputScheduler) - : this( - p => - { - var resultObservable = execute(p); - return Observable.Defer( - () => - { - var cancelationSubject = new Subject(); - void Cancel() => cancelationSubject.OnNext(Unit.Default); - return Observable - .Return((resultObservable.TakeUntil(cancelationSubject), (Action)Cancel)); - }); - }, - canExecute, - outputScheduler ?? RxSchedulers.MainThreadScheduler) - { - } - - /// - /// Specifies markers used to indicate the boundaries and result point of an execution process. - /// - /// Use this enumeration to identify the start, result, or end of an execution sequence when - /// processing or tracking execution flow. - private enum ExecutionDemarcation - { - Begin, - - Result, - - End - } - - /// - public override IObservable CanExecute => _canExecute; - - /// - public override IObservable IsExecuting => _isExecuting; - - /// - public override IObservable ThrownExceptions => _exceptions.AsObservable(); - - /// - public override IObservable Execute(TParam parameter) - { - try - { - return Observable.Defer( - () => - { - _executionInfo.OnNext(ExecutionInfo.CreateBegin()); - return Observable<(IObservable, Action)>.Empty; - }) - .Concat(_execute(parameter)) - .SelectMany(sourceAndCancellation => - { - var (sourceObservable, cancelCallback) = sourceAndCancellation; - var sharedSource = sourceObservable.Publish().RefCount(2); - - // This is the subscription that survives for however long sourceObservable takes to complete (or fail). - sharedSource - .Do(result => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateResult(result))) - .Catch( - ex => - { - _exceptions.OnNext(ex); - return Observable.Empty(); - }) - .Finally(() => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd())) - .Subscribe(); - - // TODO: Check if it is a problem that we always cancel, even on normal completion!!! - return sharedSource.Finally(() => cancelCallback()); - }); - } - catch (Exception ex) - { - _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd()); - _exceptions.OnNext(ex); - return Observable.Throw(ex); - } - } - - /// - public override IObservable Execute() => Execute(default!); - - /// - public override IDisposable Subscribe(IObserver observer) => - _results.Subscribe(observer); - - /// - protected override void Dispose(bool disposing) - { - if (!disposing) - { - return; - } - - _canExecuteSubscription.Dispose(); - } - - /// - /// Represents information about a specific stage and result of an execution process. - /// - /// The ExecutionInfo struct encapsulates both the demarcation point within an execution flow and - /// the associated result value. It is typically used to track or communicate the current execution state and its - /// outcome in scenarios such as workflow processing or state machines. - private readonly struct ExecutionInfo - { - private ExecutionInfo(ExecutionDemarcation demarcation, TResult result) - { - Demarcation = demarcation; - Result = result; - } - - public ExecutionDemarcation Demarcation { get; } - - public TResult Result { get; } - - public static ExecutionInfo CreateBegin() => - new(ExecutionDemarcation.Begin, default!); - - public static ExecutionInfo CreateResult(TResult result) => - new(ExecutionDemarcation.Result, result); - - public static ExecutionInfo CreateEnd() => - new(ExecutionDemarcation.End, default!); - } -} diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs index 70ad9888db..6e56b279b2 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs @@ -1,10 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Windows.Input; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -25,7 +27,7 @@ namespace ReactiveUI; /// /// /// To create an instance of ReactiveCommand, call one of the static creation methods defined by this class. -/// can be used when your execution logic is synchronous. +/// ReactiveCommand.Create can be used when your execution logic is synchronous. /// ReactiveCommand.CreateFromObservable and /// ReactiveCommand.CreateFromTask (and overloads) can be used for asynchronous /// execution logic. Optionally, you can provide an observable that governs the availability of the command for execution, @@ -69,7 +71,14 @@ namespace ReactiveUI; /// public abstract class ReactiveCommandBase : IReactiveCommand, ICommand { + /// + /// Backing field for the CanExecuteChanged event. + /// private EventHandler? _canExecuteChanged; + + /// + /// Tracks the most recent CanExecute value for ICommand compatibility. + /// private bool _canExecuteValue; /// @@ -80,22 +89,13 @@ event EventHandler? ICommand.CanExecuteChanged } /// - public abstract IObservable CanExecute - { - get; - } + public abstract IObservable CanExecute { get; } /// - public abstract IObservable IsExecuting - { - get; - } + public abstract IObservable IsExecuting { get; } /// - public abstract IObservable ThrownExceptions - { - get; - } + public abstract IObservable ThrownExceptions { get; } /// public void Dispose() @@ -158,19 +158,18 @@ protected void OnCanExecuteChanged(bool newValue) /// The parameter being passed to the ICommand. protected virtual void ICommandExecute(object? parameter) { - // ensure that null is coerced to default(TParam) so that commands taking value types will use a sensible default if no parameter is supplied parameter ??= default(TParam); - if (parameter is not null && parameter is not TParam) + if (parameter is not null and not TParam) { throw new InvalidOperationException( - $"Command requires parameters of type {typeof(TParam).FullName}, but received parameter of type {parameter.GetType().FullName}."); + $"Command requires parameters of type {typeof(TParam).FullName}, but received {nameof(parameter)} of type {parameter.GetType().FullName}."); } var result = parameter is null ? Execute() : Execute((TParam)parameter); - result - .Catch(Observable.Empty) - .Subscribe(); + // Fire-and-forget: drive the execution to completion and swallow any error here + // (errors are surfaced through ThrownExceptions). Replaces .Catch(Empty).Subscribe(). + result.Subscribe(new DelegateObserver(static _ => { }, static _ => { })); } } diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs index c372944cff..0fc2df4b3b 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommandMixins.cs @@ -1,10 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Linq.Expressions; using System.Windows.Input; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -14,188 +17,317 @@ namespace ReactiveUI; /// /// InvokeCommand is typically chained after an that represents user intent. It /// forwards each value into an once returns true, -/// keeping the observable and command lifetimes aligned via the returned disposable. +/// keeping the observable and command lifetimes aligned via the returned disposable. Each overload is implemented as a +/// small tailored sink that tracks the current command / can-execute state and invokes on each source value when the +/// command is currently able to execute; there is no operator chain. /// /// -/// -/// -/// x.ViewModel.SaveCommand) -/// .Select(_ => Unit.Default) -/// .InvokeCommand(ViewModel.SaveCommand) -/// .DisposeWith(disposables); -/// ]]> -/// -/// public static class ReactiveCommandMixins { /// /// Subscribes to the observable sequence and invokes the specified command for each element, if the command can /// execute with the element as its parameter. /// - /// The command's CanExecuteChanged event is monitored to ensure that the command is only - /// executed when it is able to do so. If the command is null, no action is taken for elements in the sequence. - /// Disposing the returned IDisposable will unsubscribe from the sequence and stop further command - /// invocations. /// The type of the elements in the observable sequence and the parameter type for the command. /// The observable sequence whose elements are passed to the command as parameters. - /// The command to invoke for each element in the sequence. The command is executed only if its CanExecute method - /// returns true for the element. This parameter can be null. - /// An IDisposable object that can be used to unsubscribe from the observable sequence and stop invoking the - /// command. - public static IDisposable InvokeCommand(this IObservable item, ICommand? command) - { - var canExecuteChanged = Observable.FromEvent( - eventHandler => - { - void Handler(object? sender, EventArgs e) => eventHandler(Unit.Default); - return Handler; - }, - h => command!.CanExecuteChanged += h, - h => command!.CanExecuteChanged -= h) - .StartWith(Unit.Default); - - return WithLatestFromFixed(item, canExecuteChanged, (value, _) => new InvokeCommandInfo(command, command!.CanExecute(value), value)) - .Where(ii => ii.CanExecute) - .Do(ii => command?.Execute(ii.Value)) - .Subscribe(); - } + /// The command to invoke for each element. Executed only if its returns true. May be null. + /// An that unsubscribes from the sequence and stops invoking the command. + public static IDisposable InvokeCommand(this IObservable item, ICommand? command) => + item.Subscribe(new DelegateObserver(value => + { + if (command?.CanExecute(value) != true) + { + return; + } + + command.Execute(value); + })); /// /// Subscribes to the observable sequence and invokes the specified reactive command for each element, if the /// command can execute. /// - /// The command is only executed for elements where its CanExecute observable returns true. If - /// the command's execution results in an error, the error is suppressed and processing continues with subsequent - /// elements. /// The type of the elements in the source observable sequence. /// The type of the result produced by the reactive command. /// The source observable sequence whose elements are used as input to the command. - /// The reactive command to invoke for each element in the sequence. Cannot be null. - /// An IDisposable that can be disposed to unsubscribe from the sequence and stop invoking the command. - /// Thrown if the command parameter is null. - public static IDisposable InvokeCommand(this IObservable item, ReactiveCommandBase? command) => + /// The reactive command to invoke for each element. Cannot be null. + /// An that unsubscribes from the sequence and stops invoking the command. + /// Thrown if is null. + public static IDisposable InvokeCommand( + this IObservable item, + ReactiveCommandBase? command) => command is null ? throw new ArgumentNullException(nameof(command)) - : WithLatestFromFixed(item, command.CanExecute, (value, canExecute) => new InvokeCommandInfo, T>(command, canExecute, value)) - .Where(ii => ii.CanExecute) - .SelectMany(ii => command.Execute(ii.Value).Catch(Observable.Empty)) - .Subscribe(); + : new ReactiveCommandInvoker(command).Run(item); /// - /// Subscribes to the observable sequence and invokes the specified command on the target object whenever a new - /// value is emitted, if the command can execute with that value. + /// Subscribes to the observable sequence and invokes the command found on the target object for each element, if + /// the command can execute with that element. /// - /// The command is only executed if it is not null and its CanExecute method returns true for the - /// emitted value. The subscription listens for changes to the command property and to the command's - /// CanExecuteChanged event. This method uses reflection to evaluate the command property expression, which may be - /// affected by trimming in some deployment scenarios. /// The type of the values emitted by the observable sequence. - /// The type of the target object that contains the command property. Must be a reference type. - /// The observable sequence whose emitted values will be passed to the command as parameters. + /// The type of the target object that contains the command property. + /// The observable sequence whose emitted values are passed to the command as parameters. /// The target object that contains the command property. Can be null. - /// An expression that identifies the command property on the target object to be invoked. The expression should - /// return an object implementing ICommand. - /// An IDisposable that can be used to unsubscribe from the observable sequence and stop invoking the command. + /// An expression identifying the command property on the target object. + /// An that unsubscribes from the sequence and stops invoking the command. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static IDisposable InvokeCommand(this IObservable item, TTarget? target, Expression> commandProperty) - where TTarget : class - { - var commandObs = target.WhenAnyValue(commandProperty); - var commandCanExecuteChanged = commandObs - .Select(command => command is null ? - Observable.Empty : - Observable.FromEvent( - eventHandler => (_, _) => eventHandler(command), - h => command.CanExecuteChanged += h, - h => command.CanExecuteChanged -= h) - .StartWith(command)) - .Switch(); - - return WithLatestFromFixed(item, commandCanExecuteChanged, (value, cmd) => new InvokeCommandInfo(cmd, cmd.CanExecute(value), value)) - .Where(ii => ii.CanExecute) - .Do(ii => ii.Command.Execute(ii.Value)) - .Subscribe(); - } + public static IDisposable InvokeCommand( + this IObservable item, + TTarget? target, + Expression> commandProperty) + where TTarget : class => + new CommandPropertyInvoker(target.WhenAnyValue(commandProperty)).Run(item); /// - /// Subscribes to the specified observable and invokes a reactive command on the target object whenever a new value - /// is emitted. + /// Subscribes to the observable sequence and invokes the reactive command found on the target object for each + /// element, if the command can execute. /// - /// The command is only executed if it is not null and its CanExecute observable returns for the current value. If the command is null or cannot execute, no action is taken for that - /// value. This method uses reflection to evaluate the command property expression, which may be affected by - /// trimming in some deployment scenarios. /// The type of the values emitted by the source observable. /// The type of the result produced by the reactive command. /// The type of the target object that contains the reactive command. - /// The observable sequence whose emitted values will be passed to the command for execution. - /// The target object that contains the reactive command to be invoked. Can be null. - /// An expression that identifies the reactive command property on the target object to be invoked. - /// An IDisposable that can be disposed to unsubscribe from the observable and stop invoking the command. + /// The observable sequence whose emitted values are passed to the command for execution. + /// The target object that contains the reactive command. Can be null. + /// An expression identifying the reactive command property on the target object. + /// An that unsubscribes from the sequence and stops invoking the command. [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static IDisposable InvokeCommand(this IObservable item, TTarget? target, Expression?>> commandProperty) - where TTarget : class + public static IDisposable InvokeCommand( + this IObservable item, + TTarget? target, + Expression?>> commandProperty) + where TTarget : class => + new ReactiveCommandPropertyInvoker(target.WhenAnyValue(commandProperty)).Run(item); + + /// + /// Tracks the latest can-execute state of a reactive command and executes it for each source value while it can. + /// + /// The command parameter type. + /// The command result type. + /// The command to invoke. + private sealed class ReactiveCommandInvoker(ReactiveCommandBase command) : IDisposable { - var command = target.WhenAnyValue(commandProperty); - var invocationInfo = command - .Select(cmd => cmd is null ? - Observable, T>>.Empty : - cmd - .CanExecute - .Select(canExecute => new InvokeCommandInfo, T>(cmd, canExecute))) - .Switch(); - - return WithLatestFromFixed(item, invocationInfo, (value, ii) => ii.WithValue(value)) - .Where(ii => ii.CanExecute) - .SelectMany(ii => ii.Command.Execute(ii.Value).Catch(Observable.Empty)) - .Subscribe(); + /// Guards the latest can-execute value. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// Subscription to the command's can-execute observable. + private IDisposable? _canExecuteSubscription; + + /// Subscription to the source sequence. + private IDisposable? _itemSubscription; + + /// The latest can-execute value. + private bool _canExecute; + + /// Starts tracking can-execute and the source sequence. + /// The source sequence. + /// A disposable stopping both subscriptions. + public ReactiveCommandInvoker Run(IObservable item) + { + _canExecuteSubscription = command.CanExecute.Subscribe(new DelegateObserver(OnCanExecute)); + _itemSubscription = item.Subscribe(new DelegateObserver(OnItem)); + return this; + } + + /// + public void Dispose() + { + _canExecuteSubscription?.Dispose(); + _itemSubscription?.Dispose(); + } + + /// Records the latest can-execute value. + /// The can-execute value. + private void OnCanExecute(bool value) + { + lock (_gate) + { + _canExecute = value; + } + } + + /// Executes the command for a source value when it can currently execute. + /// The source value passed to the command. + private void OnItem(T value) + { + bool canExecute; + lock (_gate) + { + canExecute = _canExecute; + } + + if (!canExecute) + { + return; + } + + command.Execute(value).Subscribe(new DelegateObserver(static _ => { }, static _ => { })); + } } - // See https://github.com/Reactive-Extensions/Rx.NET/issues/444 - private static IObservable WithLatestFromFixed( - IObservable item, - IObservable other, - Func resultSelector) => - item - .Publish( - os => - other - .Select( - a => - os - .Select(b => resultSelector(b, a))) - .Switch()); + /// + /// Tracks the latest exposed by a target property and invokes it for each source value + /// when it can execute with that value. + /// + /// The command parameter type. + /// The stream of current commands. + private sealed class CommandPropertyInvoker(IObservable commands) : IDisposable + { + /// Guards the latest command reference. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// Subscription to the command-property stream. + private IDisposable? _commandSubscription; + + /// Subscription to the source sequence. + private IDisposable? _itemSubscription; + + /// The latest command exposed by the property. + private ICommand? _command; + + /// Starts tracking the command property and the source sequence. + /// The source sequence. + /// A disposable stopping both subscriptions. + public CommandPropertyInvoker Run(IObservable item) + { + _commandSubscription = commands.Subscribe(new DelegateObserver(OnCommand)); + _itemSubscription = item.Subscribe(new DelegateObserver(OnItem)); + return this; + } + + /// + public void Dispose() + { + _commandSubscription?.Dispose(); + _itemSubscription?.Dispose(); + } + + /// Records the latest command exposed by the property. + /// The current command. + private void OnCommand(ICommand? command) + { + lock (_gate) + { + _command = command; + } + } + + /// Executes the current command for a source value when it can execute with that value. + /// The source value passed to the command. + private void OnItem(T value) + { + ICommand? command; + lock (_gate) + { + command = _command; + } + + if (command?.CanExecute(value) != true) + { + return; + } + + command.Execute(value); + } + } /// - /// Represents the result of invoking a command, including the command instance, whether it can be executed, and an - /// associated value. + /// Tracks the latest reactive command exposed by a target property and its can-execute state, invoking it for + /// each source value while it can. /// - /// The type of the command being invoked. - /// The type of the value associated with the command invocation. - private readonly struct InvokeCommandInfo + /// The command parameter type. + /// The command result type. + /// The stream of current commands. + private sealed class ReactiveCommandPropertyInvoker(IObservable?> commands) : IDisposable { - public InvokeCommandInfo(TCommand command, bool canExecute, TValue value) + /// Guards the latest command reference and its can-execute value. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The current command's can-execute subscription; swapped when the command changes. + private readonly SwapDisposable _canExecuteSubscription = new(); + + /// Subscription to the command-property stream. + private IDisposable? _commandSubscription; + + /// Subscription to the source sequence. + private IDisposable? _itemSubscription; + + /// The latest command exposed by the property. + private ReactiveCommandBase? _command; + + /// The latest can-execute value of the current command. + private bool _canExecute; + + /// Starts tracking the command property and the source sequence. + /// The source sequence. + /// A disposable stopping every subscription. + public ReactiveCommandPropertyInvoker Run(IObservable item) { - Command = command; - CanExecute = canExecute; - Value = value!; + _commandSubscription = commands.Subscribe(new DelegateObserver?>(OnCommand)); + _itemSubscription = item.Subscribe(new DelegateObserver(OnItem)); + return this; } - public InvokeCommandInfo(TCommand command, bool canExecute) + /// + public void Dispose() { - Command = command; - CanExecute = canExecute; - Value = default!; + _commandSubscription?.Dispose(); + _itemSubscription?.Dispose(); + _canExecuteSubscription.Dispose(); } - public TCommand Command { get; } + /// Tracks the latest command and switches the can-execute subscription to it. + /// The current command. + private void OnCommand(ReactiveCommandBase? command) + { + lock (_gate) + { + _command = command; + _canExecute = false; + } - public bool CanExecute { get; } + _canExecuteSubscription.Disposable = + command?.CanExecute.Subscribe(new DelegateObserver(OnCanExecute)); + } - public TValue Value { get; } + /// Records the latest can-execute value of the current command. + /// The can-execute value. + private void OnCanExecute(bool value) + { + lock (_gate) + { + _canExecute = value; + } + } - public InvokeCommandInfo WithValue(TValue value) => - new(Command, CanExecute, value); + /// Executes the current command for a source value when it can currently execute. + /// The source value passed to the command. + private void OnItem(T value) + { + ReactiveCommandBase? command; + bool canExecute; + lock (_gate) + { + command = _command; + canExecute = _canExecute; + } + + if (command is null || !canExecute) + { + return; + } + + command.Execute(value).Subscribe(new DelegateObserver(static _ => { }, static _ => { })); + } } } diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand{TParam,TResult}.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand{TParam,TResult}.cs new file mode 100644 index 0000000000..f04f8b0af6 --- /dev/null +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand{TParam,TResult}.cs @@ -0,0 +1,589 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// +/// Encapsulates a user interaction behind a reactive interface. +/// +/// +/// This class provides the bulk of the actual implementation for reactive commands. You should not create instances +/// of this class directly, but rather via the static creation methods on the non-generic +/// class. The execution state (, , results and exceptions) is held in +/// inline fields and fanned out through lightweight streams rather than subjects; the +/// canExecute / isExecuting state is recomputed inline (userCanExecute && inFlight == 0) under a single gate. +/// State transitions driven by execution begin/end, results and exceptions are scheduled onto the output scheduler; +/// values returned directly from are delivered on the execution thread. +/// +/// The type of parameter values passed in during command execution. +/// The type of the values that are the result of command execution. +public class ReactiveCommand : ReactiveCommandBase +{ + /// Guards every observer-state and command-state field; held only across snapshots and field writes. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The scheduler that begin/end transitions, results and exceptions are delivered on. + private readonly IScheduler _outputScheduler; + + /// The execution function producing, per parameter, an observable of the result observable and a cancel callback. + private readonly Func Result, Action Cancel)>> _execute; + + /// Subscription to the user-supplied canExecute observable. + private readonly IDisposable _canExecuteSubscription; + + /// Observers of ; replays the current effective value on subscribe. + private readonly IObservable _canExecute; + + /// Observers of ; replays the current value on subscribe. + private readonly IObservable _isExecuting; + + /// Observers of . + private readonly IObservable _thrownExceptions; + + /// Broadcaster for the effective canExecute value. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _canExecuteBroadcaster; + + /// Broadcaster for the isExecuting value. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _isExecutingBroadcaster; + + /// Broadcaster for thrown exceptions. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _exceptionsBroadcaster; + + /// Broadcaster for result values delivered to command subscribers. + [SuppressMessage("Major Code Smell", "S3459:Unassigned members should be removed", Justification = "Mutated in place via Broadcaster methods.")] + private Broadcaster _resultsBroadcaster; + + /// The latest value from the user-supplied canExecute observable (false until it first emits). + private bool _userCanExecute; + + /// The current effective canExecute value (userCanExecute && inFlight == 0). + private bool _canExecuteValue; + + /// The current isExecuting value (inFlight > 0). + private bool _isExecutingValue; + + /// The number of executions currently in flight. + private int _inFlight; + + /// Latched once the command has been disposed. + private bool _disposed; + + /// + /// Initializes a new instance of the class for work that + /// signals cancellation through a separate callback (as opposed to cancelling by unsubscribing). + /// + /// The function producing the result observable and a cancellation callback per execution. + /// An observable governing whether the command can execute. + /// The scheduler on which output is delivered. + /// Thrown if is . + protected internal ReactiveCommand( + Func Result, Action Cancel)>> execute, + IObservable? canExecute, + IScheduler? outputScheduler) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _outputScheduler = outputScheduler ?? RxSchedulers.MainThreadScheduler; + _canExecute = new BoolStream(this, isExecuting: false); + _isExecuting = new BoolStream(this, isExecuting: true); + _thrownExceptions = new ExceptionStream(this); + _canExecuteSubscription = (canExecute ?? SingleValueObservable.True) + .Subscribe(new DelegateObserver(OnUserCanExecute, OnUserCanExecuteError)); + } + + /// + /// Initializes a new instance of the class. + /// + /// The function producing the result observable per execution. + /// An observable governing whether the command can execute. + /// The scheduler on which output is delivered. + /// Thrown if is . + protected internal ReactiveCommand( + Func> execute, + IObservable? canExecute, + IScheduler? outputScheduler) + : this( + p => new SingleValueObservable<(IObservable Result, Action Cancel)>((execute(p), NoCancel)), + canExecute, + outputScheduler) + { + } + + /// + public override IObservable CanExecute => _canExecute; + + /// + public override IObservable IsExecuting => _isExecuting; + + /// + public override IObservable ThrownExceptions => _thrownExceptions; + + /// + public override IObservable Execute(TParam parameter) => new ExecuteObservable(this, parameter); + + /// + public override IObservable Execute() => Execute(default!); + + /// + public override IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + lock (_gate) + { + _resultsBroadcaster.Add(observer); + } + + return new ResultSubscription(this, observer); + } + + /// + protected override void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + lock (_gate) + { + _disposed = true; + } + + _canExecuteSubscription.Dispose(); + } + + /// The no-op cancellation callback used when cancellation happens by unsubscribing. + private static void NoCancel() + { + // Intentionally empty: cancellation is performed by disposing the subscription, so no callback work is needed. + } + + /// Records the latest user canExecute value and recomputes the effective state. + /// The value emitted by the user canExecute observable. + private void OnUserCanExecute(bool value) + { + bool changed; + bool effective; + lock (_gate) + { + if (_disposed) + { + return; + } + + _userCanExecute = value; + effective = value && _inFlight == 0; + changed = effective != _canExecuteValue; + if (changed) + { + _canExecuteValue = effective; + } + } + + if (!changed) + { + return; + } + + OnCanExecuteChanged(effective); + _canExecuteBroadcaster.Next(effective); + } + + /// Routes a canExecute error to the exceptions stream and treats the command as not executable. + /// The error from the user canExecute observable. + private void OnUserCanExecuteError(Exception error) + { + DeliverException(error); + OnUserCanExecute(false); + } + + /// Schedules an execution-begin transition on the output scheduler. + private void NotifyBegin() => + _outputScheduler.Schedule(this, static (_, command) => + { + command.ApplyBegin(); + return EmptyDisposable.Instance; + }); + + /// Schedules an execution-end transition on the output scheduler. + private void NotifyEnd() => + _outputScheduler.Schedule(this, static (_, command) => + { + command.ApplyEnd(); + return EmptyDisposable.Instance; + }); + + /// Schedules a result broadcast to command subscribers on the output scheduler. + /// The result value to broadcast. + private void NotifyResult(TResult result) => + _outputScheduler.Schedule((Command: this, Result: result), static (_, state) => + { + state.Command._resultsBroadcaster.Next(state.Result); + return EmptyDisposable.Instance; + }); + + /// Schedules exception delivery on the output scheduler. + /// The exception to deliver. + private void DeliverException(Exception error) => + _outputScheduler.Schedule((Command: this, Error: error), static (_, state) => + { + state.Command.ApplyException(state.Error); + return EmptyDisposable.Instance; + }); + + /// Applies an execution-begin: increments in-flight count and updates isExecuting / canExecute. + private void ApplyBegin() + { + bool isExecutingChanged; + bool canExecuteChanged; + bool canExecuteValue; + lock (_gate) + { + isExecutingChanged = _inFlight == 0; + _inFlight++; + if (isExecutingChanged) + { + _isExecutingValue = true; + } + + canExecuteValue = _userCanExecute && _inFlight == 0; + canExecuteChanged = canExecuteValue != _canExecuteValue; + if (canExecuteChanged) + { + _canExecuteValue = canExecuteValue; + } + } + + if (isExecutingChanged) + { + _isExecutingBroadcaster.Next(true); + } + + if (!canExecuteChanged) + { + return; + } + + OnCanExecuteChanged(canExecuteValue); + _canExecuteBroadcaster.Next(canExecuteValue); + } + + /// Applies an execution-end: decrements in-flight count and updates isExecuting / canExecute. + private void ApplyEnd() + { + bool isExecutingChanged; + bool canExecuteChanged; + bool canExecuteValue; + lock (_gate) + { + if (_inFlight > 0) + { + _inFlight--; + } + + isExecutingChanged = _inFlight == 0 && _isExecutingValue; + if (isExecutingChanged) + { + _isExecutingValue = false; + } + + canExecuteValue = _userCanExecute && _inFlight == 0; + canExecuteChanged = canExecuteValue != _canExecuteValue; + if (canExecuteChanged) + { + _canExecuteValue = canExecuteValue; + } + } + + if (isExecutingChanged) + { + _isExecutingBroadcaster.Next(false); + } + + if (!canExecuteChanged) + { + return; + } + + OnCanExecuteChanged(canExecuteValue); + _canExecuteBroadcaster.Next(canExecuteValue); + } + + /// Delivers an exception to subscribers, or to the default handler when there are none. + /// The exception to deliver. + private void ApplyException(Exception error) + { + bool hasObservers; + lock (_gate) + { + hasObservers = _exceptionsBroadcaster.HasObservers; + } + + if (hasObservers) + { + _exceptionsBroadcaster.Next(error); + return; + } + + RxState.DefaultExceptionHandler.OnNext(error); + } + + /// Removes a results observer. Called when a command subscription is disposed. + /// The observer to remove. + private void RemoveResult(IObserver observer) + { + lock (_gate) + { + _resultsBroadcaster.Remove(observer); + } + } + + /// Adds a canExecute / isExecuting observer and returns the value to replay to it. + /// The observer to add. + /// for the isExecuting stream; otherwise the canExecute stream. + /// The current value to replay to the new observer. + private bool AddBool(IObserver observer, bool isExecuting) + { + lock (_gate) + { + if (isExecuting) + { + _isExecutingBroadcaster.Add(observer); + return _isExecutingValue; + } + + _canExecuteBroadcaster.Add(observer); + return _canExecuteValue; + } + } + + /// Removes a canExecute / isExecuting observer. + /// The observer to remove. + /// for the isExecuting stream; otherwise the canExecute stream. + private void RemoveBool(IObserver observer, bool isExecuting) + { + lock (_gate) + { + if (isExecuting) + { + _isExecutingBroadcaster.Remove(observer); + return; + } + + _canExecuteBroadcaster.Remove(observer); + } + } + + /// Adds an exceptions observer. + /// The observer to add. + private void AddException(IObserver observer) + { + lock (_gate) + { + _exceptionsBroadcaster.Add(observer); + } + } + + /// Removes an exceptions observer. + /// The observer to remove. + private void RemoveException(IObserver observer) + { + lock (_gate) + { + _exceptionsBroadcaster.Remove(observer); + } + } + + /// The / stream; replays the current value on subscribe. + /// The owning command. + /// for isExecuting; otherwise canExecute. + private sealed class BoolStream(ReactiveCommand owner, bool isExecuting) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var current = owner.AddBool(observer, isExecuting); + observer.OnNext(current); + return new BoolSubscription(owner, observer, isExecuting); + } + } + + /// Unsubscribes a canExecute / isExecuting observer on dispose. + /// The owning command. + /// The subscribed observer. + /// Which stream the observer belongs to. + private sealed class BoolSubscription(ReactiveCommand owner, IObserver observer, bool isExecuting) : IDisposable + { + /// + public void Dispose() => owner.RemoveBool(observer, isExecuting); + } + + /// The stream. + /// The owning command. + private sealed class ExceptionStream(ReactiveCommand owner) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + owner.AddException(observer); + return new ExceptionSubscription(owner, observer); + } + } + + /// Unsubscribes an exceptions observer on dispose. + /// The owning command. + /// The subscribed observer. + private sealed class ExceptionSubscription(ReactiveCommand owner, IObserver observer) : IDisposable + { + /// + public void Dispose() => owner.RemoveException(observer); + } + + /// Unsubscribes a results observer on dispose. + /// The owning command. + /// The subscribed observer. + private sealed class ResultSubscription(ReactiveCommand owner, IObserver observer) : IDisposable + { + /// + public void Dispose() => owner.RemoveResult(observer); + } + + /// The cold observable returned by ; each subscription runs one execution. + /// The owning command. + /// The parameter for this execution. + private sealed class ExecuteObservable(ReactiveCommand owner, TParam parameter) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Execution(owner, observer).Run(parameter); + } + } + + /// + /// Drives a single execution: begins, subscribes to the produced result observable, forwards results to the + /// caller (raw) while scheduling result/exception/end notifications, and balances the begin with exactly one end. + /// + /// The owning command. + /// The observer subscribed to this execution. + private sealed class Execution(ReactiveCommand owner, IObserver downstream) + : IObserver<(IObservable Result, Action Cancel)>, IDisposable + { + /// Subscription to the execution-source observable (the result/cancel tuple producer). + private IDisposable? _outer; + + /// Subscription to the produced result observable. + private IDisposable? _inner; + + /// The cancellation callback supplied by the execution source. + private Action? _cancel; + + /// Guards the once-only execution-end (0 = not fired, 1 = fired). + private int _endFired; + + /// Begins the execution and subscribes to the execution source. + /// The parameter for this execution. + /// A disposable that cancels the execution. + public IDisposable Run(TParam parameter) + { + owner.NotifyBegin(); + try + { + _outer = owner._execute(parameter).Subscribe(this); + } + catch (Exception ex) + { + EndOnce(); + owner.DeliverException(ex); + downstream.OnError(ex); + return EmptyDisposable.Instance; + } + + return this; + } + + /// + public void OnNext((IObservable Result, Action Cancel) value) + { + _cancel = value.Cancel; + _inner = value.Result.Subscribe(new ResultObserver(this)); + } + + /// + public void OnError(Exception error) + { + EndOnce(); + owner.DeliverException(error); + downstream.OnError(error); + } + + /// + public void OnCompleted() + { + } + + /// + public void Dispose() + { + _inner?.Dispose(); + _outer?.Dispose(); + _cancel?.Invoke(); + EndOnce(); + } + + /// Schedules the balancing execution-end exactly once. + private void EndOnce() + { + if (Interlocked.Exchange(ref _endFired, 1) != 0) + { + return; + } + + owner.NotifyEnd(); + } + + /// Forwards a produced result to the caller (raw) and broadcasts it to command subscribers. + /// The produced result. + private void OnResult(TResult value) + { + downstream.OnNext(value); + owner.NotifyResult(value); + } + + /// Completes this execution once the result observable completes. + private void OnResultCompleted() + { + EndOnce(); + downstream.OnCompleted(); + } + + /// Forwards the produced result observable's notifications back into the execution. + /// The owning execution. + private sealed class ResultObserver(Execution execution) : IObserver + { + /// + public void OnNext(TResult value) => execution.OnResult(value); + + /// + public void OnError(Exception error) => execution.OnError(error); + + /// + public void OnCompleted() => execution.OnResultCompleted(); + } + } +} diff --git a/src/ReactiveUI/ReactiveObject/IReactiveObject.cs b/src/ReactiveUI/ReactiveObject/IReactiveObject.cs index 6a91c877ae..32a6f1a276 100644 --- a/src/ReactiveUI/ReactiveObject/IReactiveObject.cs +++ b/src/ReactiveUI/ReactiveObject/IReactiveObject.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/ReactiveObject/IReactiveObjectExtensions.cs b/src/ReactiveUI/ReactiveObject/IReactiveObjectExtensions.cs index 3bfb6d7d18..865824fb4c 100644 --- a/src/ReactiveUI/ReactiveObject/IReactiveObjectExtensions.cs +++ b/src/ReactiveUI/ReactiveObject/IReactiveObjectExtensions.cs @@ -1,10 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; using System.Runtime.CompilerServices; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -14,8 +17,10 @@ namespace ReactiveUI; public static class IReactiveObjectExtensions { #if NETSTANDARD || NETFRAMEWORK + /// Stores per-instance extension state keyed by reactive object. private static readonly ConditionalWeakTable> state = new(); #else + /// Stores per-instance extension state keyed by reactive object. private static readonly ConditionalWeakTable> state = []; #endif @@ -45,37 +50,37 @@ private interface IExtensionState /// Subscribe raise property changing events to a property changing /// observable. Must be called before raising property changing events. /// - void SubscribePropertyChangingEvents(); + void SubscribeChanging(); /// /// Raises a property changing event. /// /// The name of the property that is changing. - void RaisePropertyChanging(string propertyName); + void RaiseChanging(string propertyName); /// /// Subscribe raise property changed events to a property changed /// observable. Must be called before raising property changed events. /// - void SubscribePropertyChangedEvents(); + void SubscribeChanged(); /// /// Raises a property changed event. /// /// The name of the property that has changed. - void RaisePropertyChanged(string propertyName); + void RaiseChanged(string propertyName); /// /// Indicates if we are currently sending change notifications. /// /// If change notifications are being sent. - bool AreChangeNotificationsEnabled(); + bool NotificationsEnabled(); /// /// Suppress change notifications until the return value is disposed. /// /// A IDisposable which when disposed will re-enable change notifications. - IDisposable SuppressChangeNotifications(); + IDisposable Suppress(); /// /// Are change notifications currently delayed. Used for Observables change notifications only. @@ -87,7 +92,7 @@ private interface IExtensionState /// Delay change notifications until the return value is disposed. /// /// A IDisposable which when disposed will re-enable change notifications. - IDisposable DelayChangeNotifications(); + IDisposable Delay(); } /// @@ -104,6 +109,7 @@ private interface IExtensionState /// The name of the property, usually /// automatically provided through the CallerMemberName attribute. /// The newly set value, normally discarded. + [SuppressMessage("Design", "CA1045:Do not pass types by reference", Justification = "By default for this operator.")] public static TRet RaiseAndSetIfChanged( this TObj reactiveObject, ref TRet backingField, @@ -118,9 +124,9 @@ public static TRet RaiseAndSetIfChanged( return newValue; } - reactiveObject.RaisingPropertyChanging(propertyName!); + reactiveObject.RaisingPropertyChanging(propertyName); backingField = newValue; - reactiveObject.RaisingPropertyChanged(propertyName!); + reactiveObject.RaisingPropertyChanged(propertyName); return newValue; } @@ -134,13 +140,17 @@ public static TRet RaiseAndSetIfChanged( /// A string representing the name of the property that has been changed. /// Leave null to let the runtime set to caller member name. /// - public static void RaisePropertyChanged(this TSender reactiveObject, [CallerMemberName] string? propertyName = null) + public static void RaisePropertyChanged( + this TSender reactiveObject, + [CallerMemberName] string? propertyName = null) where TSender : IReactiveObject { - if (propertyName is not null) + if (propertyName is null) { - reactiveObject.RaisingPropertyChanged(propertyName); + return; } + + reactiveObject.RaisingPropertyChanged(propertyName); } /// @@ -153,13 +163,17 @@ public static void RaisePropertyChanged(this TSender reactiveObject, [C /// A string representing the name of the property that has been changed. /// Leave null to let the runtime set to caller member name. /// - public static void RaisePropertyChanging(this TSender reactiveObject, [CallerMemberName] string? propertyName = null) + public static void RaisePropertyChanging( + this TSender reactiveObject, + [CallerMemberName] string? propertyName = null) where TSender : IReactiveObject { - if (propertyName is not null) + if (propertyName is null) { - reactiveObject.RaisingPropertyChanging(propertyName); + return; } + + reactiveObject.RaisingPropertyChanging(propertyName); } /// @@ -171,9 +185,11 @@ public static void RaisePropertyChanging(this TSender reactiveObject, [ public static void SubscribePropertyChangingEvents(this TSender reactiveObject) where TSender : IReactiveObject { - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); - s.SubscribePropertyChangingEvents(); + s.SubscribeChanging(); } /// @@ -185,9 +201,11 @@ public static void SubscribePropertyChangingEvents(this TSender reactiv public static void SubscribePropertyChangedEvents(this TSender reactiveObject) where TSender : IReactiveObject { - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); - s.SubscribePropertyChangedEvents(); + s.SubscribeChanged(); } /// @@ -200,11 +218,14 @@ public static void SubscribePropertyChangedEvents(this TSender reactive /// The reactive object to observe for property change notifications. Cannot be null. /// An observable sequence of property change event arguments for the specified reactive object. The sequence emits /// a value each time a property changes. - internal static IObservable> GetChangedObservable(this TSender reactiveObject) + internal static IObservable> GetChangedObservable( + this TSender reactiveObject) where TSender : IReactiveObject { - var val = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); - return val.Changed.Cast>(); + var val = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); + return new ChangeArgsCastObservable(val.Changed); } /// @@ -217,11 +238,14 @@ internal static IObservable> GetChang /// The reactive object to observe for property changing notifications. Must implement IReactiveObject. /// An observable sequence of IReactivePropertyChangedEventArgs{TSender} that emits a value each time a property on /// the reactive object is about to change. - internal static IObservable> GetChangingObservable(this TSender reactiveObject) + internal static IObservable> GetChangingObservable( + this TSender reactiveObject) where TSender : IReactiveObject { - var val = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); - return val.Changing.Cast>(); + var val = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); + return new ChangeArgsCastObservable(val.Changing); } /// @@ -238,7 +262,9 @@ internal static IObservable> GetChang internal static IObservable GetThrownExceptionsObservable(this TSender reactiveObject) where TSender : IReactiveObject { - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); return s.ThrownExceptions; } @@ -253,9 +279,11 @@ internal static void RaisingPropertyChanging(this TSender reactiveObjec { ArgumentExceptionHelper.ThrowIfNull(propertyName); - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); - s.RaisePropertyChanging(propertyName); + s.RaiseChanging(propertyName); } /// @@ -269,9 +297,11 @@ internal static void RaisingPropertyChanged(this TSender reactiveObject { ArgumentExceptionHelper.ThrowIfNull(propertyName); - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); - s.RaisePropertyChanged(propertyName); + s.RaiseChanged(propertyName); } /// @@ -287,9 +317,11 @@ internal static void RaisingPropertyChanged(this TSender reactiveObject internal static IDisposable SuppressChangeNotifications(this TSender reactiveObject) where TSender : IReactiveObject { - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); - return s.SuppressChangeNotifications(); + return s.Suppress(); } /// @@ -301,9 +333,11 @@ internal static IDisposable SuppressChangeNotifications(this TSender re internal static bool AreChangeNotificationsEnabled(this TSender reactiveObject) where TSender : IReactiveObject { - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); - return s.AreChangeNotificationsEnabled(); + return s.NotificationsEnabled(); } /// @@ -321,9 +355,11 @@ internal static bool AreChangeNotificationsEnabled(this TSender reactiv internal static IDisposable DelayChangeNotifications(this TSender reactiveObject) where TSender : IReactiveObject { - var s = state.GetValue(reactiveObject, _ => (IExtensionState)new ExtensionState(reactiveObject)); + var s = state.GetValue( + reactiveObject, + _ => (IExtensionState)(object)new ExtensionState(reactiveObject)); - return s.DelayChangeNotifications(); + return s.Delay(); } /// @@ -336,18 +372,32 @@ internal static IDisposable DelayChangeNotifications(this TSender react /// handling. /// The type of the reactive object that this extension state is associated with. Must implement . - private class ExtensionState : IExtensionState + private sealed class ExtensionState : IExtensionState where TSender : IReactiveObject { - private readonly Lazy> _thrownExceptions = new(static () => new ScheduledSubject(Scheduler.Immediate, RxState.DefaultExceptionHandler)); - private readonly Lazy> _startOrStopDelayingChangeNotifications = new(); + /// Lazily initialized subject for routing thrown exceptions to subscribers. + private readonly Lazy> _thrownExceptions = new(static () => + new ScheduledSubject(Scheduler.Immediate, RxState.DefaultExceptionHandler)); + + /// The reactive object that owns this extension state. private readonly TSender _sender; - private readonly Lazy<(ISubject> subject, IObservable> observable)> _changing; - private readonly Lazy<(ISubject> subject, IObservable> observable)> _changed; - private readonly Lazy>> _propertyChanging; - private readonly Lazy>> _propertyChanged; + /// Lazily initialized delayable sink for property changing notifications (also the Changing observable). + private readonly Lazy>> _changing; + + /// Lazily initialized delayable sink for property changed notifications (also the Changed observable). + private readonly Lazy>> _changed; + + /// Lazily initialized delayable sink that raises PropertyChanging events. + private readonly Lazy>> _propertyChanging; + + /// Lazily initialized delayable sink that raises PropertyChanged events. + private readonly Lazy>> _propertyChanged; + + /// Reference count of active change notification suppressions; zero means notifications are enabled. private long _changeNotificationsSuppressed; + + /// Reference count of active change notification delays; greater than zero means notifications are buffered. private long _changeNotificationsDelayed; /// @@ -359,8 +409,12 @@ public ExtensionState(TSender sender) _sender = sender; _changing = CreateLazyDelayableSubjectAndObservable(); _changed = CreateLazyDelayableSubjectAndObservable(); - _propertyChanging = CreateLazyDelayableEventSubject>(_sender.RaisePropertyChanging); - _propertyChanged = CreateLazyDelayableEventSubject>(_sender.RaisePropertyChanged); + _propertyChanging = + CreateLazyDelayableEventSubject>( + _sender.RaisePropertyChanging); + _propertyChanged = + CreateLazyDelayableEventSubject>( + _sender.RaisePropertyChanged); } /// @@ -370,7 +424,7 @@ public ExtensionState(TSender sender) /// change. This can be used to react to impending changes or to perform validation or cancellation logic. The /// sequence emits an event for each property change, providing information about the sender and the property /// being changed. - public IObservable> Changing => _changing.Value.observable; + public IObservable> Changing => _changing.Value; /// /// Gets an observable sequence that signals when a property value on the object has changed. @@ -378,7 +432,7 @@ public ExtensionState(TSender sender) /// Subscribers receive notifications each time a property on the object changes. The /// event arguments provide details about the sender and the property that changed. This observable does not /// emit notifications for changes that do not affect property values. - public IObservable> Changed => _changed.Value.observable; + public IObservable> Changed => _changed.Value; /// /// Gets an observable sequence of exceptions that have been thrown by the component. @@ -392,7 +446,7 @@ public ExtensionState(TSender sender) /// Determines whether change notifications are currently enabled. /// /// if change notifications are enabled; otherwise, . - public bool AreChangeNotificationsEnabled() => Interlocked.Read(ref _changeNotificationsSuppressed) == 0; + public bool NotificationsEnabled() => Interlocked.Read(ref _changeNotificationsSuppressed) == 0; /// /// Determines whether change notifications are currently delayed. @@ -411,10 +465,10 @@ public ExtensionState(TSender sender) /// /// An object that, when disposed, reenables change /// notifications. - public IDisposable SuppressChangeNotifications() + public IDisposable Suppress() { Interlocked.Increment(ref _changeNotificationsSuppressed); - return Disposable.Create(() => Interlocked.Decrement(ref _changeNotificationsSuppressed)); + return new ActionDisposable(() => Interlocked.Decrement(ref _changeNotificationsSuppressed)); } /// @@ -426,25 +480,21 @@ public IDisposable SuppressChangeNotifications() /// /// An object that, when disposed, re-enables Observable change /// notifications. - public IDisposable DelayChangeNotifications() + public IDisposable Delay() { if (Interlocked.Increment(ref _changeNotificationsDelayed) == 1) { - if (_startOrStopDelayingChangeNotifications.IsValueCreated) - { - _startOrStopDelayingChangeNotifications.Value.OnNext(Unit.Default); - } + FlushDelayed(); } - return Disposable.Create(() => + return new ActionDisposable(() => { - if (Interlocked.Decrement(ref _changeNotificationsDelayed) == 0) + if (Interlocked.Decrement(ref _changeNotificationsDelayed) != 0) { - if (_startOrStopDelayingChangeNotifications.IsValueCreated) - { - _startOrStopDelayingChangeNotifications.Value.OnNext(Unit.Default); - } + return; } + + FlushDelayed(); }); } @@ -454,7 +504,7 @@ public IDisposable DelayChangeNotifications() /// Calling this method ensures that property changing notifications are initialized and /// will be raised when applicable. This is typically used to enable change tracking or to allow external /// handlers to respond to property changes. - public void SubscribePropertyChangingEvents() => _ = _propertyChanging.Value; + public void SubscribeChanging() => _ = _propertyChanging.Value; /// /// Raises a property changing notification for the specified property. @@ -463,24 +513,25 @@ public IDisposable DelayChangeNotifications() /// notifications are only raised if change notifications are currently enabled. Use this method to support data /// binding or other scenarios where consumers need to react before a property value changes. /// The name of the property for which the change notification is raised. Cannot be null or empty. - public void RaisePropertyChanging(string propertyName) + public void RaiseChanging(string propertyName) { - if (!AreChangeNotificationsEnabled()) + if (!NotificationsEnabled()) { return; } - var changing = new ReactivePropertyChangingEventArgs(_sender, propertyName); + ReactivePropertyChangingEventArgs changing = new(_sender, propertyName); if (_propertyChanging.IsValueCreated) { - // Do not use NotifyObservable because event exceptions shouldn't be put in ThrownExceptions _propertyChanging.Value.OnNext(changing); } - if (_changing.IsValueCreated) + if (!_changing.IsValueCreated) { - NotifyObservable(_sender, changing, _changing.Value.subject); + return; } + + NotifyObservable(_sender, changing, _changing.Value); } /// @@ -489,7 +540,7 @@ public void RaisePropertyChanging(string propertyName) /// Call this method to ensure that property change notifications are set up. This is /// typically required before observing property changes through event handlers or data binding /// mechanisms. - public void SubscribePropertyChangedEvents() => _ = _propertyChanged.Value; + public void SubscribeChanged() => _ = _propertyChanged.Value; /// /// Notifies subscribers that the value of a specified property has changed. @@ -498,29 +549,31 @@ public void RaisePropertyChanging(string propertyName) /// are currently enabled. Use this method to inform listeners that a property value has been updated, typically /// within property setters. /// The name of the property that changed. Cannot be null or empty. - public void RaisePropertyChanged(string propertyName) + public void RaiseChanged(string propertyName) { - if (!AreChangeNotificationsEnabled()) + if (!NotificationsEnabled()) { return; } - var changed = new ReactivePropertyChangedEventArgs(_sender, propertyName); + ReactivePropertyChangedEventArgs changed = new(_sender, propertyName); if (_propertyChanged.IsValueCreated) { - // Do not use NotifyObservable because event exceptions shouldn't be put in ThrownExceptions _propertyChanged.Value.OnNext(changed); } - if (_changed.IsValueCreated) + if (!_changed.IsValueCreated) { - NotifyObservable(_sender, changed, _changed.Value.subject); + return; } + + NotifyObservable(_sender, changed, _changed.Value); } - /// - /// Filter a list of change notifications, returning the last change for each PropertyName in original order. - /// + /// Filters a list of change notifications, returning the last change for each PropertyName in original order. + /// The type of the change notification arguments. + /// The change notifications to filter. + /// The last change notification for each property name, in original order. private static IEnumerable DistinctEvents(IList events) where TEventArgs : IReactivePropertyChangedEventArgs { @@ -529,8 +582,8 @@ private static IEnumerable DistinctEvents(IList(); - var uniqueEvents = new Stack(events.Count); + HashSet seen = []; + Stack uniqueEvents = new(events.Count); for (var i = events.Count - 1; i >= 0; i--) { @@ -541,10 +594,31 @@ private static IEnumerable DistinctEvents(IListFlushes a lazily-created delayable sink if it has been created. + /// The sink's notification type. + /// The lazily-created sink. + private static void FlushIfCreated(Lazy> sink) + { + if (!sink.IsValueCreated) + { + return; + } + + sink.Value.Flush(); + } + + /// Flushes any notifications buffered while change notifications were delayed. + private void FlushDelayed() + { + FlushIfCreated(_propertyChanging); + FlushIfCreated(_propertyChanged); + FlushIfCreated(_changing); + FlushIfCreated(_changed); + } + /// /// Notifies the specified subject with the provided item, handling any exceptions that occur during /// notification. @@ -556,7 +630,7 @@ private static IEnumerable DistinctEvents(IListThe sender object associated with the notification. Used for logging if an exception occurs. /// The item to send to the subject's observers. /// The subject to be notified. If null, no notification is sent. - private void NotifyObservable(TSender rxObj, T item, ISubject? subject) + private void NotifyObservable(TSender rxObj, T item, DelayableNotificationSubject? subject) { try { @@ -581,22 +655,14 @@ private void NotifyObservable(TSender rxObj, T item, ISubject? subject) /// The returned observable buffers and emits change events based on the current delay /// settings. Subscribers receive only distinct change events. The subject and observable are created only when /// first accessed. - /// A lazy-initialized tuple consisting of an for IReactivePropertyChangedEventArgs<TSender> + /// A lazy-initialized tuple consisting of an for IReactivePropertyChangedEventArgs<TSender> /// for publishing change notifications and an for IReactivePropertyChangedEventArgs<TSender> /// that emits distinct change events, respecting any configured delay in change notifications. - private Lazy<(ISubject> changeSubject, IObservable> changeObservable)> CreateLazyDelayableSubjectAndObservable() => - new(() => - { - var changeSubject = new Subject>(); - var changeObservable = changeSubject - .Buffer(changeSubject.Where(_ => !AreChangeNotificationsDelayed()).Select(_ => Unit.Default) - .Merge(_startOrStopDelayingChangeNotifications.Value)) - .SelectMany(DistinctEvents) - .Publish() - .RefCount(); - - return (changeSubject, changeObservable); - }); + private Lazy>> + CreateLazyDelayableSubjectAndObservable() => + new(() => new DelayableNotificationSubject>( + AreChangeNotificationsDelayed, + DistinctEvents)); /// /// Creates a lazily initialized subject for event notifications that supports delayed change notification @@ -610,18 +676,48 @@ private void NotifyObservable(TSender rxObj, T item, ISubject? subject) /// An action to invoke for each event notification emitted by the subject. /// A Lazy object that initializes an ISubject{TEventArgs} for publishing event notifications when first /// accessed. - private Lazy> CreateLazyDelayableEventSubject(Action raiseEvent) + private Lazy> CreateLazyDelayableEventSubject(Action raiseEvent) where TEventArgs : IReactivePropertyChangedEventArgs => new(() => { - var changeSubject = new Subject(); - changeSubject - .Buffer(changeSubject.Where(_ => !AreChangeNotificationsDelayed()).Select(_ => Unit.Default) - .Merge(_startOrStopDelayingChangeNotifications.Value)) - .SelectMany(DistinctEvents) - .Subscribe(raiseEvent); - + var changeSubject = new DelayableNotificationSubject(AreChangeNotificationsDelayed, DistinctEvents); + changeSubject.Subscribe(new DelegateObserver(raiseEvent)); return changeSubject; }); } + + /// + /// Re-types the reactive object's change-argument stream from the non-generic form to + /// the caller's . Specialised to and + /// . + /// + /// The reactive object type observed. + /// The source change-argument stream. + private sealed class ChangeArgsCastObservable(IObservable> source) + : IObservable> + where TSender : IReactiveObject + { + /// + public IDisposable Subscribe(IObserver> observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// Re-types each change-argument value to the caller's sender type. + /// The observer receiving re-typed change arguments. + private sealed class Sink(IObserver> downstream) + : IObserver> + { + /// + public void OnNext(IReactivePropertyChangedEventArgs value) => + downstream.OnNext((IReactivePropertyChangedEventArgs)(object)value); + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } } diff --git a/src/ReactiveUI/ReactiveObject/ReactiveObject.cs b/src/ReactiveUI/ReactiveObject/ReactiveObject.cs index 5d52b5e54e..5e514c748c 100644 --- a/src/ReactiveUI/ReactiveObject/ReactiveObject.cs +++ b/src/ReactiveUI/ReactiveObject/ReactiveObject.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace ReactiveUI; /// @@ -13,15 +15,11 @@ namespace ReactiveUI; [DataContract] public class ReactiveObject : IReactiveNotifyPropertyChanged, IHandleObservableErrors, IReactiveObject { + /// Tracks whether PropertyChanging event subscriptions have been initialized. private bool _propertyChangingEventsSubscribed; - private bool _propertyChangedEventsSubscribed; - /// - /// Initializes a new instance of the class. - /// - public ReactiveObject() - { - } + /// Tracks whether PropertyChanged event subscriptions have been initialized. + private bool _propertyChangedEventsSubscribed; /// public event PropertyChangingEventHandler? PropertyChanging @@ -55,10 +53,20 @@ public event PropertyChangedEventHandler? PropertyChanged remove => PropertyChangedHandler -= value; } + /// Backing handler for the PropertyChanging event. [SuppressMessage("Roslynator", "RCS1159:Use EventHandler", Justification = "Long term design.")] + [SuppressMessage( + "Major Code Smell", + "S3908:Generic event handlers should be used", + Justification = "Backs the INotifyPropertyChanging.PropertyChanging interface event.")] private event PropertyChangingEventHandler? PropertyChangingHandler; + /// Backing handler for the PropertyChanged event. [SuppressMessage("Roslynator", "RCS1159:Use EventHandler", Justification = "Long term design.")] + [SuppressMessage( + "Major Code Smell", + "S3908:Generic event handlers should be used", + Justification = "Backs the INotifyPropertyChanged.PropertyChanged interface event.")] private event PropertyChangedEventHandler? PropertyChangedHandler; /// @@ -69,7 +77,8 @@ public event PropertyChangedEventHandler? PropertyChanged [Display(Order = -1, AutoGenerateField = false, AutoGenerateFilter = false)] #endif public IObservable> Changing => - Volatile.Read(ref field) ?? Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangingObservable(), null) ?? field; + Volatile.Read(ref field) ?? + Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangingObservable(), null) ?? field; /// [IgnoreDataMember] @@ -79,7 +88,8 @@ public event PropertyChangedEventHandler? PropertyChanged [Display(Order = -1, AutoGenerateField = false, AutoGenerateFilter = false)] #endif public IObservable> Changed => - Volatile.Read(ref field) ?? Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangedObservable(), null) ?? field; + Volatile.Read(ref field) ?? + Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangedObservable(), null) ?? field; /// [IgnoreDataMember] @@ -89,7 +99,8 @@ public event PropertyChangedEventHandler? PropertyChanged [Display(Order = -1, AutoGenerateField = false, AutoGenerateFilter = false)] #endif public IObservable ThrownExceptions => - Volatile.Read(ref field) ?? Interlocked.CompareExchange(ref field, this.GetThrownExceptionsObservable(), null) ?? field; + Volatile.Read(ref field) ?? + Interlocked.CompareExchange(ref field, this.GetThrownExceptionsObservable(), null) ?? field; /// void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => @@ -100,15 +111,13 @@ void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => PropertyChangedHandler?.Invoke(this, args); /// - public IDisposable SuppressChangeNotifications() => // TODO: Create Test - IReactiveObjectExtensions.SuppressChangeNotifications(this); + public IDisposable SuppressChangeNotifications() => IReactiveObjectExtensions.SuppressChangeNotifications(this); /// /// Determines if change notifications are enabled or not. /// /// A value indicating whether change notifications are enabled. - public bool AreChangeNotificationsEnabled() => // TODO: Create Test - IReactiveObjectExtensions.AreChangeNotificationsEnabled(this); + public bool AreChangeNotificationsEnabled() => IReactiveObjectExtensions.AreChangeNotificationsEnabled(this); /// /// Delays notifications until the return IDisposable is disposed. diff --git a/src/ReactiveUI/ReactiveObject/ReactiveRecord.cs b/src/ReactiveUI/ReactiveObject/ReactiveRecord.cs index 32c78f47bd..bb7382ed6a 100644 --- a/src/ReactiveUI/ReactiveObject/ReactiveRecord.cs +++ b/src/ReactiveUI/ReactiveObject/ReactiveRecord.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace ReactiveUI; /// @@ -13,15 +15,11 @@ namespace ReactiveUI; [DataContract] public record ReactiveRecord : IReactiveNotifyPropertyChanged, IHandleObservableErrors, IReactiveObject { + /// Tracks whether property-changing event subscriptions have been set up. private bool _propertyChangingEventsSubscribed; - private bool _propertyChangedEventsSubscribed; - /// - /// Initializes a new instance of the class. - /// - public ReactiveRecord() - { - } + /// Tracks whether property-changed event subscriptions have been set up. + private bool _propertyChangedEventsSubscribed; /// public event PropertyChangingEventHandler? PropertyChanging @@ -55,10 +53,20 @@ public event PropertyChangedEventHandler? PropertyChanged remove => PropertyChangedHandler -= value; } + /// Backing event store for property-changing notifications. [SuppressMessage("Roslynator", "RCS1159:Use EventHandler", Justification = "Long term design.")] + [SuppressMessage( + "Major Code Smell", + "S3908:Generic event handlers should be used", + Justification = "Backs the INotifyPropertyChanging.PropertyChanging interface event.")] private event PropertyChangingEventHandler? PropertyChangingHandler; + /// Backing event store for property-changed notifications. [SuppressMessage("Roslynator", "RCS1159:Use EventHandler", Justification = "Long term design.")] + [SuppressMessage( + "Major Code Smell", + "S3908:Generic event handlers should be used", + Justification = "Backs the INotifyPropertyChanged.PropertyChanged interface event.")] private event PropertyChangedEventHandler? PropertyChangedHandler; /// @@ -68,8 +76,9 @@ public event PropertyChangedEventHandler? PropertyChanged [Browsable(false)] [Display(Order = -1, AutoGenerateField = false, AutoGenerateFilter = false)] #endif - public IObservable> Changing => // TODO: Create Test - Volatile.Read(ref field) ?? Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangingObservable(), null) ?? field; + public IObservable> Changing => + Volatile.Read(ref field) ?? + Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangingObservable(), null) ?? field; /// [IgnoreDataMember] @@ -78,8 +87,9 @@ public event PropertyChangedEventHandler? PropertyChanged [Browsable(false)] [Display(Order = -1, AutoGenerateField = false, AutoGenerateFilter = false)] #endif - public IObservable> Changed => // TODO: Create Test - Volatile.Read(ref field) ?? Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangedObservable(), null) ?? field; + public IObservable> Changed => + Volatile.Read(ref field) ?? + Interlocked.CompareExchange(ref field, ((IReactiveObject)this).GetChangedObservable(), null) ?? field; /// [IgnoreDataMember] @@ -88,29 +98,32 @@ public event PropertyChangedEventHandler? PropertyChanged [Browsable(false)] [Display(Order = -1, AutoGenerateField = false, AutoGenerateFilter = false)] #endif - public IObservable ThrownExceptions => Volatile.Read(ref field) ?? Interlocked.CompareExchange(ref field, this.GetThrownExceptionsObservable(), null) ?? field; + public IObservable ThrownExceptions => Volatile.Read(ref field) ?? + Interlocked.CompareExchange( + ref field, + this.GetThrownExceptionsObservable(), + null) ?? field; /// - void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => PropertyChangingHandler?.Invoke(this, args); + void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => + PropertyChangingHandler?.Invoke(this, args); /// - void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => PropertyChangedHandler?.Invoke(this, args); + void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => + PropertyChangedHandler?.Invoke(this, args); /// - public IDisposable SuppressChangeNotifications() => // TODO: Create Test - IReactiveObjectExtensions.SuppressChangeNotifications(this); + public IDisposable SuppressChangeNotifications() => IReactiveObjectExtensions.SuppressChangeNotifications(this); /// /// Determines if change notifications are enabled or not. /// /// A value indicating whether change notifications are enabled. - public bool AreChangeNotificationsEnabled() => // TODO: Create Test - IReactiveObjectExtensions.AreChangeNotificationsEnabled(this); + public bool AreChangeNotificationsEnabled() => IReactiveObjectExtensions.AreChangeNotificationsEnabled(this); /// /// Delays notifications until the return IDisposable is disposed. /// /// A disposable which when disposed will send delayed notifications. - public IDisposable DelayChangeNotifications() => // TODO: Create Test - IReactiveObjectExtensions.DelayChangeNotifications(this); + public IDisposable DelayChangeNotifications() => IReactiveObjectExtensions.DelayChangeNotifications(this); } diff --git a/src/ReactiveUI/ReactiveProperty/IReactiveProperty.cs b/src/ReactiveUI/ReactiveProperty/IReactiveProperty.cs index ab4ab31c09..7aa688e632 100644 --- a/src/ReactiveUI/ReactiveProperty/IReactiveProperty.cs +++ b/src/ReactiveUI/ReactiveProperty/IReactiveProperty.cs @@ -1,9 +1,11 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections; +using System.ComponentModel; +using System.Reactive.Disposables; namespace ReactiveUI; diff --git a/src/ReactiveUI/ReactiveProperty/ReactiveProperty.cs b/src/ReactiveUI/ReactiveProperty/ReactiveProperty.cs index 97927728b0..40151df8df 100644 --- a/src/ReactiveUI/ReactiveProperty/ReactiveProperty.cs +++ b/src/ReactiveUI/ReactiveProperty/ReactiveProperty.cs @@ -1,11 +1,16 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections; +using System.ComponentModel; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; using System.Runtime.CompilerServices; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -39,12 +44,12 @@ public class ReactiveProperty : ReactiveObject, IReactiveProperty /// /// Publishes value changes to the validation pipeline. /// - private readonly Subject _checkValidation = new(); + private readonly BroadcastSubject _checkValidation = new(); /// /// Publishes "refresh" signals for the current value (used to force emission even when the value is unchanged). /// - private readonly Subject _valueRefereshed = new(); + private readonly BroadcastSubject _valueRefereshed = new(); /// /// Publishes changes without relying on reflection-based property observation. @@ -58,22 +63,23 @@ public class ReactiveProperty : ReactiveObject, IReactiveProperty /// The subject is disposed with the instance; emission sites guard against disposal using . /// /// - private readonly BehaviorSubject _valueChanged; + private readonly BehaviorBroadcastSubject _valueChanged; /// /// Holds the active validation subscription, if any. /// - private readonly SerialDisposable _validationDisposable = new(); + private readonly SwapDisposable _validationDisposable = new(); /// /// Lazily created subject that publishes the current error sequence. /// - private readonly Lazy> _errorChanged; + private readonly Lazy> _errorChanged; /// /// Stores validators registered via . /// - private readonly Lazy, IObservable>>> _validatorStore = new(static () => []); + private readonly Lazy, IObservable>>> + _validatorStore = new(static () => []); /// /// The number of initial values to skip for subscriptions created by . @@ -88,7 +94,7 @@ public class ReactiveProperty : ReactiveObject, IReactiveProperty /// /// The shared observable backing . /// - private IObservable? _observable; + private ReplayBroadcastSubject? _observable; /// /// The current value backing . @@ -147,15 +153,19 @@ public ReactiveProperty(T? initialValue, bool skipCurrentValueOnSubscribe, bool /// true to prevent the current value from being emitted to new subscribers upon subscription; otherwise, false. /// true to allow consecutive duplicate values to be published to subscribers; otherwise, false to suppress /// duplicate notifications. - public ReactiveProperty(T? initialValue, IScheduler? scheduler, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) + public ReactiveProperty( + T? initialValue, + IScheduler? scheduler, + bool skipCurrentValueOnSubscribe, + bool allowDuplicateValues) { _skipCurrentValue = skipCurrentValueOnSubscribe ? 1 : 0; _isDistinctUntilChanged = !allowDuplicateValues; _value = initialValue; _scheduler = scheduler ?? RxSchedulers.TaskpoolScheduler; - _valueChanged = new BehaviorSubject(initialValue); - _errorChanged = new Lazy>(() => new BehaviorSubject(GetErrors(null))); + _valueChanged = new(initialValue); + _errorChanged = new(() => new(GetErrors(null))); GetSubscription(); } @@ -176,6 +186,10 @@ public ReactiveProperty(T? initialValue, IScheduler? scheduler, bool skipCurrent /// notification. [DataMember] [JsonInclude] + [SuppressMessage( + "Critical Bug", + "S4275:Getters and setters should access the expected fields", + Justification = "Setter writes _value via SetValue().")] public T? Value { get => _value; @@ -185,7 +199,6 @@ public T? Value { if (!_isDistinctUntilChanged) { - // Preserve existing semantics: identical assignment produces a "refresh" emission when duplicates are allowed. _valueRefereshed.OnNext(_value); } @@ -208,7 +221,7 @@ public T? Value /// Subscribers receive notifications whenever the set of errors is updated. The sequence emits /// the current collection of errors after each change. The observable completes when the owning object is disposed, /// if applicable. - public IObservable ObserveErrorChanged => _errorChanged.Value.AsObservable(); + public IObservable ObserveErrorChanged => _errorChanged.Value; /// /// Gets an observable sequence that signals whether the object currently has validation errors. @@ -216,7 +229,7 @@ public T? Value /// The returned observable emits a new value each time the error state changes. Subscribers /// receive the current error state immediately upon subscription, followed by updates as errors are added or /// cleared. - public IObservable ObserveHasErrors => ObserveErrorChanged.Select(_ => HasErrors); + public IObservable ObserveHasErrors => new SyncProjectObservable(ObserveErrorChanged, _ => HasErrors); /// /// Creates a new instance of ReactiveProperty without requiring RequiresUnreferencedCode attributes. @@ -243,8 +256,10 @@ public static ReactiveProperty Create(T? initialValue) /// if set to true [skip current value on subscribe]. /// if set to true [allow duplicate concurrent values]. /// A new ReactiveProperty instance. - public static ReactiveProperty Create(T? initialValue, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) - => new(initialValue, RxSchedulers.TaskpoolScheduler, skipCurrentValueOnSubscribe, allowDuplicateValues); + public static ReactiveProperty Create( + T? initialValue, + bool skipCurrentValueOnSubscribe, + bool allowDuplicateValues) => new(initialValue, RxSchedulers.TaskpoolScheduler, skipCurrentValueOnSubscribe, allowDuplicateValues); /// /// Creates a new instance of ReactiveProperty with a custom scheduler without requiring RequiresUnreferencedCode attributes. @@ -255,8 +270,20 @@ public static ReactiveProperty Create(T? initialValue, bool skipCurrentValueO /// if set to true [allow duplicate concurrent values]. /// A new ReactiveProperty instance. /// Thrown if is . - public static ReactiveProperty Create(T? initialValue, IScheduler scheduler, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues) - => new(initialValue, scheduler, skipCurrentValueOnSubscribe, allowDuplicateValues); + public static ReactiveProperty Create( + T? initialValue, + IScheduler scheduler, + bool skipCurrentValueOnSubscribe, + bool allowDuplicateValues) => new(initialValue, scheduler, skipCurrentValueOnSubscribe, allowDuplicateValues); + + /// + /// Adds a validation rule to the current ReactiveProperty instance using the specified validator function. + /// + /// A function that takes an observable sequence of property values and returns an observable sequence of validation errors. + /// The current ReactiveProperty instance with the added validation rule. + public ReactiveProperty AddValidationError( + Func, IObservable> validator) => + AddValidationError(validator, false); /// /// Adds a validation rule to the current ReactiveProperty instance using the specified validator function. @@ -269,33 +296,17 @@ public static ReactiveProperty Create(T? initialValue, IScheduler scheduler, /// true to ignore validation for the initial value of the property; otherwise, false. If true, validation will only /// occur on subsequent value changes. /// The current ReactiveProperty instance with the added validation rule. - public ReactiveProperty AddValidationError(Func, IObservable> validator, bool ignoreInitialError = false) + public ReactiveProperty AddValidationError( + Func, IObservable> validator, + bool ignoreInitialError) { _validatorStore.Value.Add(validator); var validators = _validatorStore.Value - .Select(x => x(ignoreInitialError ? _checkValidation : _checkValidation.StartWith(_value))) + .Select(x => x(ignoreInitialError ? _checkValidation : new PrependObservable(_checkValidation, _value))) .ToArray(); - _validationDisposable.Disposable = Observable - .CombineLatest(validators) - .ObserveOn(_scheduler) - .Select(xs => - { - if (xs.Count == 0 || xs.All(x => x == null)) - { - return null; - } - - var strings = xs - .Where(x => x != null) - .OfType(); - var others = xs - .Where(x => x is not null and not string) - .SelectMany(x => x!.OfType()); - - return strings.Concat(others); - }) - .Subscribe(x => + _validationDisposable.Disposable = new ValidationStream(validators, _scheduler) + .Subscribe(new DelegateObserver(x => { var lastHasErrors = HasErrors; _currentErrors = x; @@ -308,14 +319,24 @@ public ReactiveProperty AddValidationError(Func, IObservable< if (lastHasErrors != currentHasErrors) { - _scheduler.Schedule(() => this.RaisePropertyChanged(SingletonPropertyChangedEventArgs.HasErrors.PropertyName)); + _scheduler.Schedule(() => + this.RaisePropertyChanged(SingletonPropertyChangedEventArgs.HasErrors.PropertyName)); } _errorChanged.Value.OnNext(x); - }).DisposeWith(_disposables); + })); return this; } + /// + /// Adds a validation rule to the property using the specified validator function. + /// + /// A function that receives an observable sequence of property values and returns an observable sequence of validation error messages. + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError( + Func, IObservable> validator) => + AddValidationError(validator, false); + /// /// Adds a validation rule to the property using the specified validator function. /// @@ -326,23 +347,44 @@ public ReactiveProperty AddValidationError(Func, IObservable< /// validation error messages. The returned string is interpreted as the error message; a null value indicates no /// error. /// true to suppress the initial validation error until the property value changes; otherwise, false. - /// The current instance with the validation rule applied. - public ReactiveProperty AddValidationError(Func, IObservable> validator, bool ignoreInitialError = false) => - AddValidationError(xs => validator(xs).Select(x => (IEnumerable?)x), ignoreInitialError); + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError( + Func, IObservable> validator, + bool ignoreInitialError) => + AddValidationError(xs => new SyncProjectObservable(validator(xs), static x => (IEnumerable?)x), ignoreInitialError); + + /// + /// Adds asynchronous validation logic to the reactive property using the specified validator function. + /// + /// A function that asynchronously validates the current value and returns a collection of validation errors. + /// The current ReactiveProperty instance with the specified validation logic applied. + public ReactiveProperty AddValidationError( + Func> validator) => + AddValidationError(validator, false); /// /// Adds asynchronous validation logic to the reactive property using the specified validator function. /// - /// This method enables chaining of multiple validation rules on a . + /// This method enables chaining of multiple validation rules on a ReactiveProperty. /// Validation is triggered whenever the property's value changes. The validator function can perform asynchronous /// operations, such as remote checks or complex computations. /// A function that asynchronously validates the current value and returns a collection of validation errors. The /// function receives the current value as input and returns a task that produces an enumerable of validation error /// objects. If the collection is empty or null, the value is considered valid. /// true to suppress validation errors for the initial value; otherwise, false. - /// The current instance with the specified validation logic applied. - public ReactiveProperty AddValidationError(Func> validator, bool ignoreInitialError = false) => - AddValidationError(xs => xs.SelectMany(x => validator(x)), ignoreInitialError); + /// The current ReactiveProperty instance with the specified validation logic applied. + public ReactiveProperty AddValidationError( + Func> validator, + bool ignoreInitialError) => + AddValidationError(xs => new AsyncProjectObservable(xs, x => validator(x)), ignoreInitialError); + + /// + /// Adds an asynchronous validation rule to the property using the specified validator function. + /// + /// A function that asynchronously validates the property's value and returns an error message or null. + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError(Func> validator) => + AddValidationError(validator, false); /// /// Adds an asynchronous validation rule to the property using the specified validator function. @@ -352,9 +394,17 @@ public ReactiveProperty AddValidationError(Func> valid /// A function that asynchronously validates the property's value and returns an error message if validation fails, /// or null if the value is valid. /// true to suppress the initial validation error until the value changes; otherwise, false. - /// A instance with the validation rule applied. - public ReactiveProperty AddValidationError(Func> validator, bool ignoreInitialError = false) => - AddValidationError(xs => xs.SelectMany(x => validator(x)), ignoreInitialError); + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError(Func> validator, bool ignoreInitialError) => + AddValidationError(xs => new AsyncProjectObservable(xs, x => validator(x)), ignoreInitialError); + + /// + /// Adds a validation rule to the reactive property using the specified validator function. + /// + /// A function that takes the current value and returns a collection of validation errors. + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError(Func validator) => + AddValidationError(validator, false); /// /// Adds a validation rule to the reactive property using the specified validator function. @@ -364,9 +414,17 @@ public ReactiveProperty AddValidationError(Func> validator, /// A function that takes the current value and returns a collection of validation errors. Returns null or an empty /// collection if the value is valid. /// true to ignore validation errors for the initial value; otherwise, false. - /// The current instance with the validation rule applied. - public ReactiveProperty AddValidationError(Func validator, bool ignoreInitialError = false) => - AddValidationError(xs => xs.Select(x => validator(x)), ignoreInitialError); + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError(Func validator, bool ignoreInitialError) => + AddValidationError(xs => new SyncProjectObservable(xs, x => validator(x)), ignoreInitialError); + + /// + /// Adds a validation rule to the property using the specified validator function. + /// + /// A function that returns a validation error message or null if the value is valid. + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError(Func validator) => + AddValidationError(validator, false); /// /// Adds a validation rule to the property using the specified validator function. @@ -377,9 +435,9 @@ public ReactiveProperty AddValidationError(Func validator, /// A function that takes the current value of the property and returns a validation error message if the value is /// invalid; otherwise, returns null or an empty string if the value is valid. /// true to suppress validation errors for the initial value of the property; otherwise, false. - /// The current instance with the validation rule applied. - public ReactiveProperty AddValidationError(Func validator, bool ignoreInitialError = false) => - AddValidationError(xs => xs.Select(x => validator(x)), ignoreInitialError); + /// The current ReactiveProperty instance with the validation rule applied. + public ReactiveProperty AddValidationError(Func validator, bool ignoreInitialError) => + AddValidationError(xs => new SyncProjectObservable(xs, x => validator(x)), ignoreInitialError); /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. @@ -450,16 +508,18 @@ public IDisposable Subscribe(IObserver observer) { if (observer == null) { - return Disposable.Empty; + return EmptyDisposable.Instance; } if (IsDisposed) { observer.OnCompleted(); - return Disposable.Empty; + return EmptyDisposable.Instance; } - return _observable!.Subscribe(observer).DisposeWith(_disposables); + var subscription = _observable!.Subscribe(new SchedulingObserver(observer, _scheduler)); + _disposables.Add(subscription); + return subscription; } /// @@ -468,23 +528,29 @@ public IDisposable Subscribe(IObserver observer) /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { - if (_disposables?.IsDisposed == false && disposing) + if (_disposables?.IsDisposed != false || !disposing) { - _disposables?.Dispose(); + return; + } - _checkValidation.Dispose(); - _valueRefereshed.Dispose(); - _validationDisposable.Dispose(); + _disposables?.Dispose(); - _valueChanged.OnCompleted(); - _valueChanged.Dispose(); + _checkValidation.Dispose(); + _valueRefereshed.Dispose(); + _validationDisposable.Dispose(); - if (_errorChanged.IsValueCreated) - { - _errorChanged.Value.OnCompleted(); - _errorChanged.Value.Dispose(); - } + _valueChanged.OnCompleted(); + _valueChanged.Dispose(); + + _observable?.Dispose(); + + if (!_errorChanged.IsValueCreated) + { + return; } + + _errorChanged.Value.OnCompleted(); + _errorChanged.Value.Dispose(); } /// @@ -500,11 +566,13 @@ private void SetValue(T? value) { _value = value; - if (!IsDisposed) + if (IsDisposed) { - _checkValidation.OnNext(value); - _valueChanged.OnNext(value); + return; } + + _checkValidation.OnNext(value); + _valueChanged.OnNext(value); } /// @@ -521,19 +589,433 @@ private void SetValue(T? value) /// private void GetSubscription() { - IObservable source = _valueChanged; + // A replay-1 relay multicasts to all subscribers (Replay(1) + RefCount). Value changes pass through the + // skip-initial and distinct gates; refresh emissions always flow. Per-subscriber scheduler delivery is applied + // in Subscribe via SchedulingObserver, so the relay itself is the shared source. + var relay = new ReplayBroadcastSubject(1); + + _disposables.Add(_valueChanged + .Subscribe(new ValueChangeRelay(relay, _skipCurrentValue, _isDistinctUntilChanged, _checkIf))); + _disposables.Add(_valueRefereshed + .Subscribe(new DelegateObserver(relay.OnNext))); + + _observable = relay; + } + + /// Applies skip-initial and optional distinct-until-changed to value changes before forwarding them to the shared relay. + /// The shared replay relay that multicasts to subscribers. + /// The number of initial values to skip. + /// Whether consecutive equal values are suppressed. + /// The equality comparer used by the distinct gate. + private sealed class ValueChangeRelay(IObserver relay, int skipCount, bool isDistinct, EqualityComparer comparer) : IObserver + { + /// The remaining number of initial values to skip. + private int _toSkip = skipCount; + + /// The last forwarded value, used by the distinct gate. + private T? _last; + + /// Whether holds a value yet. + private bool _hasLast; + + /// + public void OnNext(T? value) + { + if (_toSkip > 0) + { + _toSkip--; + return; + } + + if (isDistinct && _hasLast && comparer.Equals(value, _last)) + { + return; + } + + _last = value; + _hasLast = true; + relay.OnNext(value); + } + + /// + public void OnError(Exception error) => relay.OnError(error); + + /// + public void OnCompleted() => relay.OnCompleted(); + } + + /// + /// Combines the latest error sequence of every validator, aggregates them, and delivers the result on the + /// property's scheduler. Fuses the validator combine-latest, aggregation, and scheduling into one sink. + /// + /// The per-validator error streams. + /// The scheduler aggregated results are delivered on. + private sealed class ValidationStream(IObservable[] validators, IScheduler scheduler) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(new SchedulingObserver(observer, scheduler), validators); + } + + /// Tracks the latest error sequence of each validator and emits their aggregate once all have reported. + private sealed class Sink : IDisposable + { + /// Guards the latest values and the arrival/completion counters. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving the aggregated errors (already scheduled). + private readonly IObserver _downstream; + + /// The latest error sequence reported by each validator. + private readonly IEnumerable?[] _latest; + + /// Whether each validator has reported at least once. + private readonly bool[] _has; + + /// The subscriptions to each validator stream. + private readonly IDisposable?[] _subscriptions; + + /// The number of validators that have reported at least once. + private int _haveCount; + + /// The number of validators that have completed. + private int _doneCount; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Initializes a new instance of the class and subscribes to every validator. + /// The observer receiving the aggregated errors. + /// The per-validator error streams. + public Sink(IObserver downstream, IObservable[] validators) + { + _downstream = downstream; + _latest = new IEnumerable?[validators.Length]; + _has = new bool[validators.Length]; + _subscriptions = new IDisposable?[validators.Length]; + for (var i = 0; i < validators.Length; i++) + { + _subscriptions[i] = validators[i].Subscribe(new Element(this, i)); + } + } + + /// + public void Dispose() + { + for (var i = 0; i < _subscriptions.Length; i++) + { + _subscriptions[i]?.Dispose(); + } + } + + /// Records a validator's latest errors and emits the aggregate once every validator has reported. + /// The validator index. + /// The validator's latest error sequence. + private void OnNextAt(int index, IEnumerable? value) + { + IEnumerable? aggregated; + lock (_gate) + { + if (_stopped) + { + return; + } + + if (!_has[index]) + { + _has[index] = true; + _haveCount++; + } + + _latest[index] = value; + if (_haveCount < _latest.Length) + { + return; + } + + if (Array.TrueForAll(_latest, static x => x is null)) + { + aggregated = null; + } + else + { + var strings = _latest.Where(static x => x is not null).OfType(); + var others = _latest + .Where(static x => x is not null and not string) + .SelectMany(static x => x!.OfType()); + aggregated = strings.Concat(others); + } + } + + _downstream.OnNext(aggregated); + } + + /// Forwards an error from any validator. + /// The error to forward. + private void OnErrorAt(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + } + + _downstream.OnError(error); + } + + /// Completes the downstream once every validator has completed. + private void OnCompletedAt() + { + lock (_gate) + { + if (_stopped || ++_doneCount < _latest.Length) + { + return; + } + + _stopped = true; + } + + _downstream.OnCompleted(); + } + + /// Routes one validator's notifications to the parent sink, tagged with its index. + /// The owning sink. + /// The validator index. + private sealed class Element(Sink parent, int index) : IObserver + { + /// + public void OnNext(IEnumerable? value) => parent.OnNextAt(index, value); + + /// + public void OnError(Exception error) => parent.OnErrorAt(error); + + /// + public void OnCompleted() => parent.OnCompletedAt(); + } + } + } + + /// Projects each value of a validator input through a synchronous selector. Specialised to the validator adapters. + /// The source element type. + /// The projected element type. + /// The source observable. + /// Projects a source value into a result. + private sealed class SyncProjectObservable(IObservable source, Func selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } - source = source.Skip(_skipCurrentValue); + /// Applies the selector to each value and forwards the result. + /// The observer receiving projected values. + /// Projects a source value into a result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver + { + /// + public void OnNext(TIn value) + { + TOut result; + try + { + result = selector(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } - if (_isDistinctUntilChanged) + /// + /// For each value of a validator input, runs an asynchronous selector and emits its result when the task completes, + /// merging concurrent results. Specialised to the asynchronous validator adapters. + /// + /// The source element type. + /// The result element type. + /// The source observable. + /// Produces a task for each source value. + private sealed class AsyncProjectObservable(IObservable source, Func> selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + var sink = new Sink(observer, selector); + return sink.Run(source); + } + + /// Runs the selector per source value and merges the task results under a gate. + private sealed class Sink : IObserver, IDisposable { - source = source.DistinctUntilChanged(); + /// Serializes downstream delivery and the active-task accounting. + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + + /// The observer receiving merged task results. + private readonly IObserver _downstream; + + /// Produces a task for each source value. + private readonly Func> _selector; + + /// The subscription to the source. + private IDisposable? _sourceSubscription; + + /// The number of in-flight tasks plus one while the source is still active. + private int _active = 1; + + /// Whether the downstream has been terminated. + private bool _stopped; + + /// Initializes a new instance of the class. + /// The observer receiving merged task results. + /// Produces a task for each source value. + public Sink(IObserver downstream, Func> selector) + { + _downstream = downstream; + _selector = selector; + } + + /// Subscribes to the source after construction so this is not exposed during construction. + /// The source observable. + /// The sink, which disposes the run. + public Sink Run(IObservable source) + { + _sourceSubscription = source.Subscribe(this); + return this; + } + + /// + public void OnNext(TIn value) + { + Task task; + try + { + task = _selector(value); + } + catch (Exception ex) + { + Fail(ex); + return; + } + + Interlocked.Increment(ref _active); + _ = ForwardAsync(task); + } + + /// + public void OnError(Exception error) => Fail(error); + + /// + public void OnCompleted() => Done(); + + /// + public void Dispose() => _sourceSubscription?.Dispose(); + + /// Awaits the selector task, forwarding its result or error, and completes the run once everything is done. + /// The selector task. + /// A task that completes when the result has been forwarded. + private async Task ForwardAsync(Task task) + { + TOut result; + try + { + result = await task.ConfigureAwait(false); + } + catch (Exception ex) + { + Fail(ex); + return; + } + + lock (_gate) + { + if (_stopped) + { + return; + } + + _downstream.OnNext(result); + } + + Done(); + } + + /// Decrements the active count and completes the downstream when the source and all tasks are finished. + private void Done() + { + if (Interlocked.Decrement(ref _active) != 0) + { + return; + } + + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + _downstream.OnCompleted(); + } + } + + /// Forwards an error to the downstream exactly once. + /// The error to forward. + private void Fail(Exception error) + { + lock (_gate) + { + if (_stopped) + { + return; + } + + _stopped = true; + _downstream.OnError(error); + } + } } + } - _observable = source - .Merge(_valueRefereshed) - .Replay(1) - .RefCount() - .ObserveOn(_scheduler); + /// Emits a leading value before forwarding the source, so a validator sees the current value first. Specialised to the validation input. + /// The source observable. + /// The value emitted before the source. + private sealed class PrependObservable(IObservable source, T? value) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + observer.OnNext(value); + return source.Subscribe(observer); + } } } diff --git a/src/ReactiveUI/ReactiveProperty/ReactivePropertyMixins.cs b/src/ReactiveUI/ReactiveProperty/ReactivePropertyMixins.cs index 9531b1a1a8..733d29d347 100644 --- a/src/ReactiveUI/ReactiveProperty/ReactivePropertyMixins.cs +++ b/src/ReactiveUI/ReactiveProperty/ReactivePropertyMixins.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Collections; +using System.Linq.Expressions; using System.Reflection; #if !XAMARINIOS && !XAMARINMAC && !XAMARINTVOS && !MONOANDROID using System.ComponentModel.DataAnnotations; @@ -32,8 +34,11 @@ public static class ReactivePropertyMixins /// An expression that selects the reactive property to be validated. This is used to discover validation attributes /// and display metadata. Cannot be null. /// The same reactive property instance with validation enabled based on the discovered validation attributes. - [RequiresUnreferencedCode("DataAnnotations validation uses reflection to discover attributes and is not trim-safe. Use manual validation for AOT scenarios.")] - public static ReactiveProperty AddValidation(this ReactiveProperty self, Expression?>> selfSelector) + [RequiresUnreferencedCode( + "DataAnnotations validation uses reflection to discover attributes and is not trim-safe. Use manual validation for AOT scenarios.")] + public static ReactiveProperty AddValidation( + this ReactiveProperty self, + Expression?>> selfSelector) { ArgumentExceptionHelper.ThrowIfNull(selfSelector); ArgumentExceptionHelper.ThrowIfNull(self); @@ -43,19 +48,17 @@ public static ReactiveProperty AddValidation(this ReactiveProperty self var display = propertyInfo.GetCustomAttribute(); var attrs = propertyInfo.GetCustomAttributes().ToArray(); - // Use the AOT-compatible constructor that doesn't require reflection for type discovery - var context = new ValidationContext(self, serviceProvider: null, items: null) + ValidationContext context = new(self, null, null) { - DisplayName = display?.GetName() ?? propertyInfo.Name, - MemberName = nameof(ReactiveProperty.Value), + DisplayName = display?.GetName() ?? propertyInfo.Name, MemberName = nameof(ReactiveProperty.Value) }; if (attrs.Length != 0) { self.AddValidationError(x => { - var validationResults = new List(); - if (System.ComponentModel.DataAnnotations.Validator.TryValidateValue(x!, context, validationResults, attrs)) + List validationResults = []; + if (Validator.TryValidateValue(x!, context, validationResults, attrs)) { return null; } @@ -80,7 +83,49 @@ public static ReactiveProperty AddValidation(this ReactiveProperty self { ArgumentExceptionHelper.ThrowIfNull(self); - return self.ObserveErrorChanged - .Select(static x => x?.OfType()?.FirstOrDefault()); + return new ValidationErrorObservable(self.ObserveErrorChanged); + } + + /// + /// A fused sink that projects each error collection to its first message (or ) — + /// replacing the Select(x => x?.OfType<string>()?.FirstOrDefault()) used by + /// . + /// + /// The source stream of error collections. + private sealed class ValidationErrorObservable(IObservable source) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// Projects each error collection to its first string message and forwards it. + private sealed class Sink(IObserver downstream) : IObserver + { + /// + public void OnNext(IEnumerable? value) + { + string? message; + try + { + message = value?.OfType()?.FirstOrDefault(); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(message); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } } } diff --git a/src/ReactiveUI/ReactiveProperty/SingletonDataErrorsChangedEventArgs.cs b/src/ReactiveUI/ReactiveProperty/SingletonDataErrorsChangedEventArgs.cs index 98a3e74221..201d71dc86 100644 --- a/src/ReactiveUI/ReactiveProperty/SingletonDataErrorsChangedEventArgs.cs +++ b/src/ReactiveUI/ReactiveProperty/SingletonDataErrorsChangedEventArgs.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/ReactiveProperty/SingletonPropertyChangedEventArgs.cs b/src/ReactiveUI/ReactiveProperty/SingletonPropertyChangedEventArgs.cs index 5e0f28ecdb..6e195d5070 100644 --- a/src/ReactiveUI/ReactiveProperty/SingletonPropertyChangedEventArgs.cs +++ b/src/ReactiveUI/ReactiveProperty/SingletonPropertyChangedEventArgs.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.ComponentModel; + namespace ReactiveUI; /// diff --git a/src/ReactiveUI/ReactiveUI.csproj b/src/ReactiveUI/ReactiveUI.csproj index c172e32277..24e749be76 100644 --- a/src/ReactiveUI/ReactiveUI.csproj +++ b/src/ReactiveUI/ReactiveUI.csproj @@ -1,4 +1,4 @@ - + $(ReactiveUIFinalTargetFrameworks) ReactiveUI @@ -21,105 +21,87 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - + - - - - - + + + + + - - + + - - + + - + - - - - + + + + - - - + + + - - - - + + + + - - + + - - - - - - - - - - + - + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ReactiveUI/Registration/DependencyResolverRegistrar.cs b/src/ReactiveUI/Registration/DependencyResolverRegistrar.cs index 906f8af161..ea5d95e3e7 100644 --- a/src/ReactiveUI/Registration/DependencyResolverRegistrar.cs +++ b/src/ReactiveUI/Registration/DependencyResolverRegistrar.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,10 +15,17 @@ namespace ReactiveUI; /// The dependency resolver used to register service instances and factories. Cannot be null. internal sealed class DependencyResolverRegistrar(IMutableDependencyResolver resolver) : IRegistrar { - private readonly IMutableDependencyResolver _resolver = resolver ?? throw new ArgumentNullException(nameof(resolver)); + /// The underlying dependency resolver used for all service registrations. + private readonly IMutableDependencyResolver _resolver = + resolver ?? throw new ArgumentNullException(nameof(resolver)); /// - public void RegisterConstant(Func factory, string? contract = null) + public void RegisterConstant(Func factory) + where TService : class => + RegisterConstant(factory, null); + + /// + public void RegisterConstant(Func factory, string? contract) where TService : class { ArgumentExceptionHelper.ThrowIfNull(factory); @@ -33,7 +40,17 @@ public void RegisterConstant(Func factory, string? contract } /// - public void RegisterLazySingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TService>(Func factory, string? contract = null) + public void RegisterLazySingleton< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TService>( + Func factory) + where TService : class => + RegisterLazySingleton(factory, null); + + /// + public void RegisterLazySingleton< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TService>( + Func factory, + string? contract) where TService : class { ArgumentExceptionHelper.ThrowIfNull(factory); @@ -48,7 +65,12 @@ public void RegisterConstant(Func factory, string? contract } /// - public void Register(Func factory, string? contract = null) + public void Register(Func factory) + where TService : class => + Register(factory, null); + + /// + public void Register(Func factory, string? contract) where TService : class { ArgumentExceptionHelper.ThrowIfNull(factory); diff --git a/src/ReactiveUI/Registration/Registrations.cs b/src/ReactiveUI/Registration/Registrations.cs index 6bd66be76c..2ad553fab8 100644 --- a/src/ReactiveUI/Registration/Registrations.cs +++ b/src/ReactiveUI/Registration/Registrations.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -20,15 +20,28 @@ public void Register(IRegistrar registrar) { ArgumentExceptionHelper.ThrowIfNull(registrar); + RegisterObservableForPropertyFactories(registrar); + RegisterStringConverters(registrar); + RegisterNullableConverters(registrar); + RegisterPlatformServices(registrar); + } + + /// Registers the default factories. + /// The Splat registrar. + private static void RegisterObservableForPropertyFactories(IRegistrar registrar) + { registrar.RegisterConstant(static () => new INPCObservableForProperty()); registrar.RegisterConstant(static () => new IROObservableForProperty()); registrar.RegisterConstant(static () => new POCOObservableForProperty()); + } - // General converters - register to Splat for backward compatibility + /// Registers the default value-to-string and string-to-value binding converters. + /// The Splat registrar. + private static void RegisterStringConverters(IRegistrar registrar) + { RegisterConverter(registrar, new EqualityTypeConverter()); RegisterConverter(registrar, new StringConverter()); - // Numeric → String converters RegisterConverter(registrar, new ByteToStringTypeConverter()); RegisterConverter(registrar, new NullableByteToStringTypeConverter()); RegisterConverter(registrar, new ShortToStringTypeConverter()); @@ -44,7 +57,6 @@ public void Register(IRegistrar registrar) RegisterConverter(registrar, new DecimalToStringTypeConverter()); RegisterConverter(registrar, new NullableDecimalToStringTypeConverter()); - // String → Numeric converters RegisterConverter(registrar, new StringToByteTypeConverter()); RegisterConverter(registrar, new StringToNullableByteTypeConverter()); RegisterConverter(registrar, new StringToShortTypeConverter()); @@ -60,55 +72,51 @@ public void Register(IRegistrar registrar) RegisterConverter(registrar, new StringToDecimalTypeConverter()); RegisterConverter(registrar, new StringToNullableDecimalTypeConverter()); - // Boolean ↔ String converters RegisterConverter(registrar, new BooleanToStringTypeConverter()); RegisterConverter(registrar, new NullableBooleanToStringTypeConverter()); RegisterConverter(registrar, new StringToBooleanTypeConverter()); RegisterConverter(registrar, new StringToNullableBooleanTypeConverter()); - // Guid ↔ String converters RegisterConverter(registrar, new GuidToStringTypeConverter()); RegisterConverter(registrar, new NullableGuidToStringTypeConverter()); RegisterConverter(registrar, new StringToGuidTypeConverter()); RegisterConverter(registrar, new StringToNullableGuidTypeConverter()); - // DateTime ↔ String converters RegisterConverter(registrar, new DateTimeToStringTypeConverter()); RegisterConverter(registrar, new NullableDateTimeToStringTypeConverter()); RegisterConverter(registrar, new StringToDateTimeTypeConverter()); RegisterConverter(registrar, new StringToNullableDateTimeTypeConverter()); - // DateTimeOffset ↔ String converters RegisterConverter(registrar, new DateTimeOffsetToStringTypeConverter()); RegisterConverter(registrar, new NullableDateTimeOffsetToStringTypeConverter()); RegisterConverter(registrar, new StringToDateTimeOffsetTypeConverter()); RegisterConverter(registrar, new StringToNullableDateTimeOffsetTypeConverter()); - // TimeSpan ↔ String converters RegisterConverter(registrar, new TimeSpanToStringTypeConverter()); RegisterConverter(registrar, new NullableTimeSpanToStringTypeConverter()); RegisterConverter(registrar, new StringToTimeSpanTypeConverter()); RegisterConverter(registrar, new StringToNullableTimeSpanTypeConverter()); #if NET6_0_OR_GREATER - // DateOnly ↔ String converters (.NET 6+) RegisterConverter(registrar, new DateOnlyToStringTypeConverter()); RegisterConverter(registrar, new NullableDateOnlyToStringTypeConverter()); RegisterConverter(registrar, new StringToDateOnlyTypeConverter()); RegisterConverter(registrar, new StringToNullableDateOnlyTypeConverter()); - // TimeOnly ↔ String converters (.NET 6+) RegisterConverter(registrar, new TimeOnlyToStringTypeConverter()); RegisterConverter(registrar, new NullableTimeOnlyToStringTypeConverter()); RegisterConverter(registrar, new StringToTimeOnlyTypeConverter()); RegisterConverter(registrar, new StringToNullableTimeOnlyTypeConverter()); #endif - // Uri ↔ String converters RegisterConverter(registrar, new UriToStringTypeConverter()); RegisterConverter(registrar, new StringToUriTypeConverter()); + } - // Nullable ↔ Non-Nullable converters + /// Registers the unidirectional value/nullable-value binding converters. + /// The Splat registrar. + private static void RegisterNullableConverters(IRegistrar registrar) + { RegisterUnidirectionalConverter(registrar); RegisterUnidirectionalConverter(registrar); RegisterUnidirectionalConverter(registrar); @@ -123,7 +131,12 @@ public void Register(IRegistrar registrar) RegisterUnidirectionalConverter(registrar); RegisterUnidirectionalConverter(registrar); RegisterUnidirectionalConverter(registrar); + } + /// Registers the default view locator, activation fetcher and command binding services. + /// The Splat registrar. + private static void RegisterPlatformServices(IRegistrar registrar) + { registrar.RegisterConstant(static () => new DefaultViewLocator()); registrar.RegisterConstant(static () => new CanActivateViewFetcher()); registrar.RegisterConstant(static () => new CreatesCommandBindingViaEvent()); @@ -147,8 +160,7 @@ private static void RegisterConverter( ArgumentExceptionHelper.ThrowIfNull(registrar); ArgumentExceptionHelper.ThrowIfNull(converter); - // Register to Splat for backward compatibility - registrar.RegisterConstant(() => converter); + registrar.RegisterConstant(() => converter); } /// @@ -165,49 +177,18 @@ private static void RegisterConverter( /// As for affinity-based discovery /// /// + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] private static void RegisterUnidirectionalConverter( IRegistrar registrar) where TConverter : IBindingTypeConverter, new() { ArgumentExceptionHelper.ThrowIfNull(registrar); - var instance = new TConverter(); - - // Register typed interface for direct type lookup + TConverter instance = new(); registrar.RegisterConstant>(() => instance); - - // Register base interface for affinity-based discovery - registrar.RegisterConstant(() => instance); - } - - /// - /// Helper method to register a bidirectional type converter with explicit generic instantiations. - /// - /// The source type. - /// The target type. - /// The converter type that handles both TFrom→TTo and TTo→TFrom conversions. - /// The dependency resolver to register with. - /// - /// This method registers the converter three times: - /// - /// As for TFrom→TTo conversion - /// As for TTo→TFrom conversion - /// As for affinity-based discovery - /// - /// - private static void RegisterBidirectionalConverter( - IRegistrar registrar) - where TConverter : IBindingTypeConverter, IBindingTypeConverter, new() - { - ArgumentExceptionHelper.ThrowIfNull(registrar); - - var instance = new TConverter(); - - // Register both generic directions - registrar.Register>(() => instance); - registrar.Register>(() => instance); - - // Register base interface for affinity-based discovery registrar.RegisterConstant(() => instance); } } diff --git a/src/ReactiveUI/Routing/MessageBus.cs b/src/ReactiveUI/Routing/MessageBus.cs index 7cc88217ec..29ee0b9709 100644 --- a/src/ReactiveUI/Routing/MessageBus.cs +++ b/src/ReactiveUI/Routing/MessageBus.cs @@ -1,9 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Globalization; +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; namespace ReactiveUI; @@ -22,8 +25,10 @@ namespace ReactiveUI; /// public class MessageBus : IMessageBus { + /// The internal store mapping type/contract pairs to weak-referenced subjects. private readonly Dictionary<(Type type, string? contract), NotAWeakReference> _messageBus = []; + /// Maps type/contract pairs to their associated scheduler overrides. private readonly Dictionary<(Type type, string? contract), IScheduler> _schedulerMappings = []; /// @@ -36,6 +41,20 @@ public class MessageBus : IMessageBus /// domain. public static IMessageBus Current { get; set; } = new MessageBus(); + /// + /// Registers a scheduler for the type, which may be specified at runtime, and the contract. + /// + /// If a scheduler is already registered for the specified runtime and contract, this will overwrite the existing registration. + /// The type of the message to listen to. + /// The scheduler on which to post the + /// notifications for the specified type and contract. CurrentThreadScheduler by default. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public void RegisterScheduler(IScheduler scheduler) => + RegisterScheduler(scheduler, null); + /// /// Registers a scheduler for the type, which may be specified at runtime, and the contract. /// @@ -46,9 +65,26 @@ public class MessageBus : IMessageBus /// A unique string to distinguish messages with /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. - public void RegisterScheduler(IScheduler scheduler, string? contract = null) => + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public void RegisterScheduler(IScheduler scheduler, string? contract) => _schedulerMappings[(typeof(T), contract)] = scheduler; + /// + /// Listen provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage. + /// + /// The type of the message to listen to. + /// An Observable representing the notifications posted to the + /// message bus. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IObservable Listen() => Listen(null); + /// /// Listen provides an Observable that will fire whenever a Message is /// provided for this object via RegisterMessageSource or SendMessage. @@ -59,14 +95,24 @@ public void RegisterScheduler(IScheduler scheduler, string? contract = null) /// only used for one purpose, leave this as null. /// An Observable representing the notifications posted to the /// message bus. - public IObservable Listen(string? contract = null) - { - return Observable.Defer(() => - { - this.Log().Info(CultureInfo.InvariantCulture, "Listening to {0}:{1}", typeof(T), contract); - return SetupSubjectIfNecessary(contract).Skip(1); - }); - } + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IObservable Listen(string? contract) => new ListenObservable(this, contract, skipFirst: true); + + /// + /// Listen provides an Observable that will fire whenever a Message is + /// provided for this object via RegisterMessageSource or SendMessage. + /// + /// The type of the message to listen to. + /// An Observable representing the notifications posted to the + /// message bus. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IObservable ListenIncludeLatest() => ListenIncludeLatest(null); /// /// Listen provides an Observable that will fire whenever a Message is @@ -78,14 +124,18 @@ public IObservable Listen(string? contract = null) /// only used for one purpose, leave this as null. /// An Observable representing the notifications posted to the /// message bus. - public IObservable ListenIncludeLatest(string? contract = null) - { - return Observable.Defer(() => - { - this.Log().Info(CultureInfo.InvariantCulture, "Listening to {0}:{1}", typeof(T), contract); - return SetupSubjectIfNecessary(contract); - }); - } + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IObservable ListenIncludeLatest(string? contract) => new ListenObservable(this, contract, skipFirst: false); + + /// + /// Determines if a particular message Type is registered. + /// + /// The Type of the message to listen to. + /// True if messages have been posted for this message Type. + public bool IsRegistered(Type type) => IsRegistered(type, null); /// /// Determines if a particular message Type is registered. @@ -95,7 +145,7 @@ public IObservable ListenIncludeLatest(string? contract = null) /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. /// True if messages have been posted for this message Type. - public bool IsRegistered(Type type, string? contract = null) + public bool IsRegistered(Type type, string? contract) { var ret = false; WithMessageBus(type, contract, (mb, item) => ret = mb.ContainsKey(item) && mb[item].IsAlive); @@ -103,6 +153,18 @@ public bool IsRegistered(Type type, string? contract = null) return ret; } + /// + /// Registers an Observable representing the stream of messages to send. + /// Another part of the code can then call Listen to retrieve this + /// Observable. + /// + /// The type of the message to listen to. + /// An Observable that will be subscribed to, and a + /// message sent out for each value provided. + /// a Disposable. + public IDisposable RegisterMessageSource(IObservable source) => + RegisterMessageSource(source, null); + /// /// Registers an Observable representing the stream of messages to send. /// Another part of the code can then call Listen to retrieve this @@ -117,18 +179,32 @@ public bool IsRegistered(Type type, string? contract = null) /// a Disposable. public IDisposable RegisterMessageSource( IObservable source, - string? contract = null) + string? contract) { ArgumentExceptionHelper.ThrowIfNull(source); var subject = SetupSubjectIfNecessary(contract); - return source.Subscribe( + return source.Subscribe(new DelegateObserver( subject.OnNext, ex => this.Log().Warn(ex, "MessageBus source for {0}:{1} terminated with an error.", typeof(T), contract), - () => this.Log().Info(CultureInfo.InvariantCulture, "MessageBus source for {0}:{1} completed.", typeof(T), contract)); + () => this.Log().Info( + CultureInfo.InvariantCulture, + "MessageBus source for {0}:{1} completed.", + typeof(T), + contract))); } + /// + /// Sends a single message using the specified Type and contract. + /// Consider using RegisterMessageSource instead if you will be sending + /// messages in response to other changes such as property changes + /// or events. + /// + /// The type of the message to send. + /// The actual message to send. + public void SendMessage(T message) => SendMessage(message, null); + /// /// Sends a single message using the specified Type and contract. /// Consider using RegisterMessageSource instead if you will be sending @@ -140,7 +216,8 @@ public IDisposable RegisterMessageSource( /// A unique string to distinguish messages with /// identical types (i.e. "MyCoolViewModel") - if the message type is /// only used for one purpose, leave this as null. - public void SendMessage(T message, string? contract = null) => SetupSubjectIfNecessary(contract).OnNext(message); + public void SendMessage(T message, string? contract) => + SetupSubjectIfNecessary(contract).OnNext(message); /// /// Ensures that a subject for the specified type and contract exists, creating and registering it if necessary. @@ -149,20 +226,24 @@ public IDisposable RegisterMessageSource( /// An optional contract string used to distinguish between different subjects of the same type. Can be null. /// An ISubject{T} instance associated with the specified type and contract. If a subject already exists, it is /// returned; otherwise, a new subject is created and registered. - private ISubject SetupSubjectIfNecessary(string? contract) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + private IReactiveSubject SetupSubjectIfNecessary(string? contract) { - ISubject? ret = null; + IReactiveSubject? ret = null; WithMessageBus(typeof(T), contract, (mb, item) => { if (mb.TryGetValue(item, out var subjRef) && subjRef.IsAlive) { - ret = (ISubject)subjRef.Target; + ret = (IReactiveSubject)subjRef.Target; return; } - ret = new ScheduledSubject(GetScheduler(item), null, new BehaviorSubject(default!)); - mb[item] = new NotAWeakReference(ret); + ret = new ScheduledSubject(GetScheduler(item), null, new BehaviorBroadcastSubject(default!)); + mb[item] = new(ret); }); return ret!; @@ -207,4 +288,61 @@ private IScheduler GetScheduler((Type type, string? contract) item) _schedulerMappings.TryGetValue(item, out var scheduler); return scheduler ?? CurrentThreadScheduler.Instance; } + + /// + /// Resolves the keyed subject lazily at subscription time and forwards it, optionally skipping the replayed + /// current value. Specialised to / . + /// + /// The message type. + /// The owning message bus. + /// The contract identifying the message channel. + /// When true, the subject's replayed current value is suppressed. + private sealed class ListenObservable(MessageBus bus, string? contract, bool skipFirst) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + IReactiveSubject source; + try + { + bus.Log().Info(CultureInfo.InvariantCulture, "Listening to {0}:{1}", typeof(T), contract); + source = bus.SetupSubjectIfNecessary(contract); + } + catch (Exception ex) + { + observer.OnError(ex); + return EmptyDisposable.Instance; + } + + return skipFirst ? source.Subscribe(new SkipFirstObserver(observer)) : source.Subscribe(observer); + } + + /// Forwards every value except the first to the downstream observer. + /// The observer receiving values after the first. + private sealed class SkipFirstObserver(IObserver downstream) : IObserver + { + /// Whether the first value has been skipped. + private bool _skipped; + + /// + public void OnNext(T value) + { + if (!_skipped) + { + _skipped = true; + return; + } + + downstream.OnNext(value); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } } diff --git a/src/ReactiveUI/Routing/NotAWeakReference.cs b/src/ReactiveUI/Routing/NotAWeakReference.cs index eeb8a76f87..6668500672 100644 --- a/src/ReactiveUI/Routing/NotAWeakReference.cs +++ b/src/ReactiveUI/Routing/NotAWeakReference.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -21,8 +21,8 @@ internal class NotAWeakReference(object target) public object Target { get; } = target; /// - /// Gets a value indicating whether the current instance is considered alive. + /// Gets a value indicating whether the current instance is considered alive. Always : this + /// type holds a strong reference to , so the target can never be collected while it is held. /// - [SuppressMessage("Microsoft.Maintainability", "CA1822", Justification = "Keep existing API.")] - public bool IsAlive => true; + public bool IsAlive => Target is not null; } diff --git a/src/ReactiveUI/Routing/RoutableViewModelMixin.cs b/src/ReactiveUI/Routing/RoutableViewModelMixin.cs index 092e499fdb..a0908735c5 100644 --- a/src/ReactiveUI/Routing/RoutableViewModelMixin.cs +++ b/src/ReactiveUI/Routing/RoutableViewModelMixin.cs @@ -1,9 +1,11 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using DynamicData; +using System.Reactive; + +using ReactiveUI.Internal; namespace ReactiveUI; @@ -18,8 +20,6 @@ namespace ReactiveUI; /// reactive navigation patterns. public static class RoutableViewModelMixin { - private static readonly ListChangeReason[] _navigationStackRemovalOperations = [ListChangeReason.Remove, ListChangeReason.RemoveRange]; - /// /// This method allows you to set up connections that only operate /// while the ViewModel has focus, and cleans up when the ViewModel @@ -39,7 +39,7 @@ public static IDisposable WhenNavigatedTo(this IRoutableViewModel item, Func + return navigationStackChanged.Subscribe(new DelegateObserver>(_ => { if (router.GetCurrentViewModel() == item) { @@ -51,7 +51,7 @@ public static IDisposable WhenNavigatedTo(this IRoutableViewModel item, Func @@ -79,12 +79,8 @@ public static IObservable WhenNavigatedToObservable(this IRoutableViewMode var router = item.HostScreen.Router; var navigationStackChanged = router.NavigationChanged.CountChanged(); - var itemRemoved = navigationStackChanged.Where(x => WasItemRemoved(x, item)); - return navigationStackChanged - .Where(_ => router.GetCurrentViewModel() == item) - .Select(_ => Unit.Default) - .TakeUntil(itemRemoved); + return new NavigatedToObservable(navigationStackChanged, router, item); } /// @@ -111,13 +107,8 @@ public static IObservable WhenNavigatingFromObservable(this IRoutableViewM var router = item.HostScreen.Router; var navigationStackChanged = router.NavigationChanged.CountChanged(); - var itemRemoved = navigationStackChanged.Where(x => WasItemRemoved(x, item)); - var viewModelsChanged = navigationStackChanged.Scan(new IRoutableViewModel?[2], (previous, _) => [previous[1], router.GetCurrentViewModel()]); - return viewModelsChanged - .Where(x => x[0] == item) - .Select(_ => Unit.Default) - .TakeUntil(itemRemoved); + return new NavigatingFromObservable(navigationStackChanged, router, item); } /// @@ -126,9 +117,214 @@ public static IObservable WhenNavigatingFromObservable(this IRoutableViewM /// The set of changes to evaluate for item removal. /// The item to check for removal within the change set. /// true if the item was removed according to the change set; otherwise, false. - private static bool WasItemRemoved(IChangeSet changeSet, IRoutableViewModel item) => - changeSet - .Any( - change => change.Reason == ListChangeReason.Clear || - (_navigationStackRemovalOperations.Contains(change.Reason) && change.Item.Current == item)); + private static bool WasItemRemoved(IReactiveChangeSet changeSet, IRoutableViewModel item) + { + // A reset/clear is flattened to one Remove per prior item, so a removal of this item (directly or via a + // clear) always appears as a Remove change carrying the item. + for (var i = 0; i < changeSet.Count; i++) + { + var change = changeSet[i]; + if (change.Reason == ReactiveChangeReason.Remove && ReferenceEquals(change.Current, item)) + { + return true; + } + } + + return false; + } + + /// + /// Emits when this view model is (or becomes) the topmost view model, and completes when it is removed from the + /// stack. Fuses the prior Where + Select + TakeUntil pipeline into one sink. + /// + /// The navigation-stack change stream. + /// The router whose current view model is inspected. + /// The view model being watched. + private sealed class NavigatedToObservable( + IObservable> source, + RoutingState router, + IRoutableViewModel item) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, router, item).Run(source); + } + + /// Emits a unit when the watched view model is current, completing once it is removed. + /// The observer receiving the focus signal. + /// The router whose current view model is inspected. + /// The view model being watched. + private sealed class Sink(IObserver downstream, RoutingState router, IRoutableViewModel item) + : IObserver>, IDisposable + { + /// The subscription to the navigation-stack change stream. + private IDisposable? _subscription; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Subscribes to the source. + /// The navigation-stack change stream. + /// The sink, which stops the run when disposed. + public Sink Run(IObservable> changes) + { + _subscription = changes.Subscribe(this); + return this; + } + + /// + public void OnNext(IReactiveChangeSet value) + { + if (_stopped) + { + return; + } + + if (WasItemRemoved(value, item)) + { + Complete(); + return; + } + + if (router.GetCurrentViewModel() != item) + { + return; + } + + downstream.OnNext(Unit.Default); + } + + /// + public void OnError(Exception error) + { + if (_stopped) + { + return; + } + + _stopped = true; + downstream.OnError(error); + } + + /// + public void OnCompleted() => Complete(); + + /// + public void Dispose() => _subscription?.Dispose(); + + /// Completes the downstream and disposes the subscription exactly once. + private void Complete() + { + if (_stopped) + { + return; + } + + _stopped = true; + downstream.OnCompleted(); + _subscription?.Dispose(); + } + } + } + + /// + /// Emits when this view model stops being the topmost view model, and completes when it is removed from the stack. + /// Fuses the prior Scan + Where + Select + TakeUntil pipeline into one sink. + /// + /// The navigation-stack change stream. + /// The router whose current view model is inspected. + /// The view model being watched. + private sealed class NavigatingFromObservable( + IObservable> source, + RoutingState router, + IRoutableViewModel item) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new Sink(observer, router, item).Run(source); + } + + /// Emits a unit when the watched view model was the previous current view model, completing on removal. + /// The observer receiving the lost-focus signal. + /// The router whose current view model is inspected. + /// The view model being watched. + private sealed class Sink(IObserver downstream, RoutingState router, IRoutableViewModel item) + : IObserver>, IDisposable + { + /// The subscription to the navigation-stack change stream. + private IDisposable? _subscription; + + /// The current view model recorded at the previous change. + private IRoutableViewModel? _previousCurrent; + + /// Whether the downstream has terminated. + private bool _stopped; + + /// Subscribes to the source. + /// The navigation-stack change stream. + /// The sink, which stops the run when disposed. + public Sink Run(IObservable> changes) + { + _subscription = changes.Subscribe(this); + return this; + } + + /// + public void OnNext(IReactiveChangeSet value) + { + if (_stopped) + { + return; + } + + if (WasItemRemoved(value, item)) + { + Complete(); + return; + } + + if (_previousCurrent == item) + { + downstream.OnNext(Unit.Default); + } + + _previousCurrent = router.GetCurrentViewModel(); + } + + /// + public void OnError(Exception error) + { + if (_stopped) + { + return; + } + + _stopped = true; + downstream.OnError(error); + } + + /// + public void OnCompleted() => Complete(); + + /// + public void Dispose() => _subscription?.Dispose(); + + /// Completes the downstream and disposes the subscription exactly once. + private void Complete() + { + if (_stopped) + { + return; + } + + _stopped = true; + downstream.OnCompleted(); + _subscription?.Dispose(); + } + } + } } diff --git a/src/ReactiveUI/Routing/RoutingState.cs b/src/ReactiveUI/Routing/RoutingState.cs index ee6f540e93..4d122777f1 100644 --- a/src/ReactiveUI/Routing/RoutingState.cs +++ b/src/ReactiveUI/Routing/RoutingState.cs @@ -1,10 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using DynamicData; -using DynamicData.Binding; +using System.Collections.ObjectModel; +using System.Reactive; +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; namespace ReactiveUI; @@ -55,16 +58,25 @@ namespace ReactiveUI; [DataContract] public class RoutingState : ReactiveObject { + /// The scheduler used to deliver navigation change notifications. [IgnoreDataMember] [JsonIgnore] private readonly IScheduler _scheduler; + /// + /// Initializes a new instance of the class using the default main thread scheduler. + /// +#pragma warning disable CS8618 + public RoutingState() + : this(null) + { + } + /// /// Initializes a new instance of the class. /// /// A scheduler for where to send navigation changes to. -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public RoutingState(IScheduler? scheduler = null) + public RoutingState(IScheduler? scheduler) { _scheduler = scheduler ?? RxSchedulers.MainThreadScheduler; NavigationStack = []; @@ -113,7 +125,7 @@ public RoutingState(IScheduler? scheduler = null) /// [IgnoreDataMember] [JsonIgnore] - public IObservable> NavigationChanged { get; protected set; } + public IObservable> NavigationChanged { get; protected set; } /// /// Sets up reactive commands and observables after deserialization. @@ -121,48 +133,136 @@ public RoutingState(IScheduler? scheduler = null) /// The streaming context for deserialization. [OnDeserialized] [RequiresUnreferencedCode("RoutingState uses ReactiveCommand which may require unreferenced code.")] - [MemberNotNull(nameof(NavigationChanged), nameof(NavigateBack), nameof(Navigate), nameof(NavigateAndReset), nameof(CurrentViewModel))] + [MemberNotNull( + nameof(NavigationChanged), + nameof(NavigateBack), + nameof(Navigate), + nameof(NavigateAndReset), + nameof(CurrentViewModel))] #if NET6_0_OR_GREATER private void SetupRx(in StreamingContext sc) => SetupRx(); #else private void SetupRx(StreamingContext sc) => SetupRx(); #endif - [MemberNotNull(nameof(NavigationChanged), nameof(NavigateBack), nameof(Navigate), nameof(NavigateAndReset), nameof(CurrentViewModel))] + /// Initializes reactive commands and observables for the navigation stack. + [MemberNotNull( + nameof(NavigationChanged), + nameof(NavigateBack), + nameof(Navigate), + nameof(NavigateAndReset), + nameof(CurrentViewModel))] private void SetupRx() { var navigateScheduler = _scheduler; NavigationChanged = NavigationStack.ToObservableChangeSet(); - var countAsBehavior = Observable.Defer(() => Observable.Return(NavigationStack.Count)).Concat(NavigationChanged.CountChanged().Select(_ => NavigationStack.Count)); + var countAsBehavior = new NavigationCountObservable(this); NavigateBack = ReactiveCommand.CreateFromObservable( - () => - { - NavigationStack.RemoveAt(NavigationStack.Count - 1); - return Observable.Return(NavigationStack.Count > 0 ? NavigationStack[NavigationStack.Count - 1] : default!).ObserveOn(navigateScheduler); - }, - countAsBehavior.Select(x => x > 1)); - - Navigate = ReactiveCommand.CreateFromObservable( - vm => - { - if (vm is null) - { - throw new Exception("Navigate must be called on an IRoutableViewModel"); - } - - NavigationStack.Add(vm); - return Observable.Return(vm).ObserveOn(navigateScheduler); - }); - - NavigateAndReset = ReactiveCommand.CreateFromObservable( - vm => - { - NavigationStack.Clear(); - return Navigate.Execute(vm); - }); - - CurrentViewModel = NavigationChanged.Select(_ => NavigationStack.LastOrDefault()!); + () => + { + NavigationStack.RemoveAt(NavigationStack.Count - 1); + return new ScheduledValueObservable( + NavigationStack.Count > 0 ? NavigationStack[^1] : null!, + navigateScheduler); + }, + new SelectObservable(countAsBehavior, static x => x > 1)); + + Navigate = ReactiveCommand.CreateFromObservable(vm => + { + if (vm is null) + { + throw new InvalidOperationException("Navigate must be called on an IRoutableViewModel"); + } + + NavigationStack.Add(vm); + return new ScheduledValueObservable(vm, navigateScheduler); + }); + + NavigateAndReset = ReactiveCommand.CreateFromObservable(vm => + { + NavigationStack.Clear(); + return Navigate.Execute(vm); + }); + + CurrentViewModel = new SelectObservable, IRoutableViewModel>( + NavigationChanged, + _ => NavigationStack.LastOrDefault()!); + } + + /// Emits a single value delivered on a scheduler. Replaces Observable.Return(value).ObserveOn(scheduler). + /// The element type. + /// The value to emit. + /// The scheduler the value is delivered on. + private sealed class ScheduledValueObservable(T value, IScheduler scheduler) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return new SingleValueObservable(value).Subscribe(new SchedulingObserver(observer, scheduler)); + } + } + + /// + /// Emits the navigation stack's current count on subscription and the new count after each navigation change. + /// Replaces the prior Observable.Defer(...).Concat(...CountChanged()...) behavior. + /// + /// The owning routing state. + private sealed class NavigationCountObservable(RoutingState owner) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + observer.OnNext(owner.NavigationStack.Count); + return owner.NavigationChanged.CountChanged() + .Subscribe(new DelegateObserver>(_ => observer.OnNext(owner.NavigationStack.Count))); + } + } + + /// Projects each value of a source through a selector. Specialised routing projection. + /// The source element type. + /// The projected element type. + /// The source observable. + /// Projects a source value into a result. + private sealed class SelectObservable(IObservable source, Func selector) : IObservable + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer, selector)); + } + + /// Applies the selector to each value and forwards the result. + /// The observer receiving projected values. + /// Projects a source value into a result. + private sealed class Sink(IObserver downstream, Func selector) : IObserver + { + /// + public void OnNext(TIn value) + { + TOut result; + try + { + result = selector(value); + } + catch (Exception ex) + { + downstream.OnError(ex); + return; + } + + downstream.OnNext(result); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } } } diff --git a/src/ReactiveUI/Routing/RoutingStateMixins.cs b/src/ReactiveUI/Routing/RoutingStateMixins.cs index a7b6860091..07cb181108 100644 --- a/src/ReactiveUI/Routing/RoutingStateMixins.cs +++ b/src/ReactiveUI/Routing/RoutingStateMixins.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -17,6 +17,10 @@ public static class RoutingStateMixins /// The instance whose navigation stack is searched. /// The first view model of type found in the navigation stack, or /// if no such view model exists. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static T? FindViewModelInStack(this RoutingState item) where T : IRoutableViewModel { diff --git a/src/ReactiveUI/RxCacheSize.cs b/src/ReactiveUI/RxCacheSize.cs index 1a8a8bd97d..737edcfbd4 100644 --- a/src/ReactiveUI/RxCacheSize.cs +++ b/src/ReactiveUI/RxCacheSize.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -12,16 +12,41 @@ namespace ReactiveUI; public static class RxCacheSize { #if ANDROID || IOS + /// + /// Default small cache limit for mobile platforms. + /// private const int DefaultSmallCacheLimit = 32; + + /// + /// Default big cache limit for mobile platforms. + /// private const int DefaultBigCacheLimit = 64; #else + /// + /// Default small cache limit for desktop platforms. + /// private const int DefaultSmallCacheLimit = 64; + + /// + /// Default big cache limit for desktop platforms. + /// private const int DefaultBigCacheLimit = 256; #endif + /// + /// The configured small cache limit. + /// private static int _smallCacheLimit; + + /// + /// The configured big cache limit. + /// private static int _bigCacheLimit; - private static int _initialized; // 0 = false, 1 = true + + /// + /// Tracks whether initialization has occurred; 0 means uninitialized, 1 means initialized. + /// + private static int _initialized; /// /// Gets the small cache limit used for internal memoizing caches. @@ -56,11 +81,13 @@ public static int BigCacheLimit /// The big cache limit to use. internal static void Initialize(int smallCacheLimit, int bigCacheLimit) { - if (Interlocked.CompareExchange(ref _initialized, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _initialized, 1, 0) != 0) { - _smallCacheLimit = smallCacheLimit; - _bigCacheLimit = bigCacheLimit; + return; } + + _smallCacheLimit = smallCacheLimit; + _bigCacheLimit = bigCacheLimit; } /// @@ -82,9 +109,11 @@ internal static void ResetForTesting() /// private static void EnsureInitialized() { - if (Interlocked.CompareExchange(ref _initialized, 0, 0) == 0) + if (Interlocked.CompareExchange(ref _initialized, 0, 0) != 0) { - Initialize(DefaultSmallCacheLimit, DefaultBigCacheLimit); + return; } + + Initialize(DefaultSmallCacheLimit, DefaultBigCacheLimit); } } diff --git a/src/ReactiveUI/RxSchedulers.cs b/src/ReactiveUI/RxSchedulers.cs index a85b696f59..b9b6647c91 100644 --- a/src/ReactiveUI/RxSchedulers.cs +++ b/src/ReactiveUI/RxSchedulers.cs @@ -1,8 +1,9 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; using System.Runtime.CompilerServices; namespace ReactiveUI; @@ -17,14 +18,23 @@ namespace ReactiveUI; /// public static class RxSchedulers { + /// + /// Lock object used to synchronize access to mutable scheduler fields. + /// #if NET9_0_OR_GREATER private static readonly Lock _lock = new(); #else private static readonly object _lock = new(); #endif + /// + /// The current main-thread scheduler, or null if not yet assigned. + /// private static volatile IScheduler? _mainThreadScheduler; + /// + /// The current task-pool scheduler, or null if not yet assigned. + /// private static volatile IScheduler? _taskpoolScheduler; /// @@ -44,6 +54,10 @@ static RxSchedulers() /// should be run "on the UI thread". In normal mode, this will be /// DispatcherScheduler. This defaults to DefaultScheduler.Instance. /// + [SuppressMessage( + "Maintainability", + "CA1508:Avoid dead conditional code", + Justification = "Double-checked locking; another thread may set the field between the outer check and the lock.")] public static IScheduler MainThreadScheduler { get @@ -72,6 +86,10 @@ public static IScheduler MainThreadScheduler /// Gets or sets the scheduler used to schedule work items to /// run in a background thread. This defaults to TaskPoolScheduler.Default. /// + [SuppressMessage( + "Maintainability", + "CA1508:Avoid dead conditional code", + Justification = "Double-checked locking; another thread may set the field between the outer check and the lock.")] public static IScheduler TaskpoolScheduler { get @@ -141,6 +159,6 @@ internal static void ResetForTesting() [MethodImpl(MethodImplOptions.NoOptimization)] internal static void EnsureStaticConstructorRun() { - // NB: This method only exists to invoke the static constructor if needed + // Intentionally empty: calling this forces the type's static constructor to run. } } diff --git a/src/ReactiveUI/RxState.cs b/src/ReactiveUI/RxState.cs index 2c46d3da95..a4d1f0c8d6 100644 --- a/src/ReactiveUI/RxState.cs +++ b/src/ReactiveUI/RxState.cs @@ -1,9 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Diagnostics; +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; namespace ReactiveUI; @@ -15,8 +18,15 @@ namespace ReactiveUI; /// handling is required. Most application code does not need to interact with this class directly. public static class RxState { + /// + /// The configured exception handler, or null if not yet initialized. + /// private static IObserver? _defaultExceptionHandler; - private static int _exceptionHandlerInitialized; // 0 = false, 1 = true + + /// + /// Tracks whether the exception handler has been initialized; 0 = uninitialized, 1 = initialized. + /// + private static int _exceptionHandlerInitialized; /// /// Gets the default exception handler for unhandled errors in ReactiveUI observables. @@ -41,10 +51,13 @@ public static IObserver DefaultExceptionHandler /// The custom exception handler to use. internal static void InitializeExceptionHandler(IObserver exceptionHandler) { - if (Interlocked.CompareExchange(ref _exceptionHandlerInitialized, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _exceptionHandlerInitialized, 1, 0) != 0) { - _defaultExceptionHandler = exceptionHandler ?? throw new ArgumentNullException(nameof(exceptionHandler)); + return; } + + ArgumentExceptionHelper.ThrowIfNull(exceptionHandler); + _defaultExceptionHandler = exceptionHandler; } /// @@ -66,25 +79,23 @@ internal static void ResetForTesting() /// private static void InitializeDefaultExceptionHandler() { - if (Interlocked.CompareExchange(ref _exceptionHandlerInitialized, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _exceptionHandlerInitialized, 1, 0) != 0) + { + return; + } + + _defaultExceptionHandler = new DelegateObserver(ex => { - _defaultExceptionHandler = Observer.Create(ex => + if (Debugger.IsAttached) { - // NB: If you're seeing this, it means that an - // ObservableAsPropertyHelper or the CanExecute of a - // ReactiveCommand ended in an OnError. Instead of silently - // breaking, ReactiveUI will halt here if a debugger is attached. - if (Debugger.IsAttached) - { - Debugger.Break(); - } + Debugger.Break(); + } -#pragma warning disable CA1065 // Avoid exceptions in constructors -- In scheduler. - RxSchedulers.MainThreadScheduler.Schedule(() => throw new UnhandledErrorException( - "An object implementing IHandleObservableErrors (often a ReactiveCommand or ObservableAsPropertyHelper) has errored, thereby breaking its observable pipeline. To prevent this, ensure the pipeline does not error, or Subscribe to the ThrownExceptions property of the object in question to handle the erroneous case.", - ex)); -#pragma warning restore CA1065 - }); - } + RxSchedulers.MainThreadScheduler.Schedule(() => throw new UnhandledErrorException( + "An object implementing IHandleObservableErrors (often a ReactiveCommand or ObservableAsPropertyHelper) has errored," + + " thereby breaking its observable pipeline. To prevent this, ensure the pipeline does not error, or Subscribe to the " + + "ThrownExceptions property of the object in question to handle the erroneous case.", + ex)); + }); } } diff --git a/src/ReactiveUI/RxSuspension.cs b/src/ReactiveUI/RxSuspension.cs index 6a37c65cae..94d1bfab23 100644 --- a/src/ReactiveUI/RxSuspension.cs +++ b/src/ReactiveUI/RxSuspension.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -15,8 +15,15 @@ namespace ReactiveUI; /// access to lifecycle events. Most applications interact with the suspension host through higher-level APIs. public static class RxSuspension { + /// + /// The current suspension host instance. + /// private static ISuspensionHost? _suspensionHost; - private static int _suspensionHostInitialized; // 0 = false, 1 = true + + /// + /// Tracks whether the suspension host has been initialized; 0 means false, 1 means true. + /// + private static int _suspensionHostInitialized; /// /// Gets the suspension host for application lifecycle management. @@ -42,10 +49,12 @@ public static ISuspensionHost SuspensionHost /// The custom suspension host to use. internal static void InitializeSuspensionHost(ISuspensionHost suspensionHost) { - if (Interlocked.CompareExchange(ref _suspensionHostInitialized, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _suspensionHostInitialized, 1, 0) != 0) { - _suspensionHost = suspensionHost ?? throw new ArgumentNullException(nameof(suspensionHost)); + return; } + + _suspensionHost = suspensionHost ?? throw new ArgumentNullException(nameof(suspensionHost)); } /// @@ -67,9 +76,11 @@ internal static void ResetForTesting() /// private static void InitializeDefaultSuspensionHost() { - if (Interlocked.CompareExchange(ref _suspensionHostInitialized, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _suspensionHostInitialized, 1, 0) != 0) { - _suspensionHost = new SuspensionHost(); + return; } + + _suspensionHost = new SuspensionHost(); } } diff --git a/src/ReactiveUI/Scheduler/ScheduledSubject.cs b/src/ReactiveUI/Scheduler/ScheduledSubject.cs index 2f60f28b62..14a16652bc 100644 --- a/src/ReactiveUI/Scheduler/ScheduledSubject.cs +++ b/src/ReactiveUI/Scheduler/ScheduledSubject.cs @@ -1,21 +1,53 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// /// A subject which dispatches all its events on the specified Scheduler. /// /// The type of item being dispatched by the Subject. -public class ScheduledSubject : ISubject, IDisposable +public class ScheduledSubject : IReactiveSubject, IDisposable { - private readonly IObserver _defaultObserver; + /// The observer that receives notifications when no other subscribers are active. + private readonly IObserver? _defaultObserver; + + /// The scheduler used to dispatch items to observers. private readonly IScheduler _scheduler; - private readonly ISubject _subject; + + /// The underlying subject that items are forwarded through. + private readonly IReactiveSubject _subject; + + /// The current count of active observers. private int _observerRefCount; - private IDisposable _defaultObserverSub = Disposable.Empty; + + /// The subscription connecting the subject to the default observer. + private IDisposable _defaultObserverSub = EmptyDisposable.Instance; + + /// + /// Initializes a new instance of the class with only a scheduler. + /// + /// The scheduler where to dispatch items to. + public ScheduledSubject(IScheduler scheduler) + : this(scheduler, null, null) + { + } + + /// + /// Initializes a new instance of the class with a scheduler and a default observer. + /// + /// The scheduler where to dispatch items to. + /// A default observer where notifications will be sent when no other subscribers are active. + public ScheduledSubject(IScheduler scheduler, IObserver? defaultObserver) + : this(scheduler, defaultObserver, null) + { + } /// /// Initializes a new instance of the class. @@ -23,16 +55,21 @@ public class ScheduledSubject : ISubject, IDisposable /// The scheduler where to dispatch items to. /// A optional default observer where notifications will be sent. /// A optional default subject which this Subject will wrap. - public ScheduledSubject(IScheduler scheduler, IObserver? defaultObserver = null, ISubject? defaultSubject = null) + public ScheduledSubject( + IScheduler scheduler, + IObserver? defaultObserver, + IReactiveSubject? defaultSubject) { _scheduler = scheduler; - _defaultObserver = defaultObserver ?? new Subject(); - _subject = defaultSubject ?? new Subject(); + _defaultObserver = defaultObserver; + _subject = defaultSubject ?? new BroadcastSubject(); - if (defaultObserver is not null) + if (defaultObserver is null) { - _defaultObserverSub = _subject.ObserveOn(_scheduler).Subscribe(_defaultObserver); + return; } + + _defaultObserverSub = _subject.Subscribe(new SchedulingObserver(defaultObserver, _scheduler)); } /// @@ -43,10 +80,10 @@ public void Dispose() } /// - public void OnCompleted() => _subject.OnCompleted(); // TODO: Create Test + public void OnCompleted() => _subject.OnCompleted(); /// - public void OnError(Exception error) => _subject.OnError(error); // TODO: Create Test + public void OnError(Exception error) => _subject.OnError(error); /// public void OnNext(T value) => _subject.OnNext(value); @@ -54,19 +91,13 @@ public void Dispose() /// public IDisposable Subscribe(IObserver observer) { - Interlocked.Exchange(ref _defaultObserverSub, Disposable.Empty).Dispose(); + ArgumentExceptionHelper.ThrowIfNull(observer); + Interlocked.Exchange(ref _defaultObserverSub, EmptyDisposable.Instance).Dispose(); Interlocked.Increment(ref _observerRefCount); - return new CompositeDisposable( - _subject.ObserveOn(_scheduler).Subscribe(observer), - Disposable.Create(() => - { - if (Interlocked.Decrement(ref _observerRefCount) <= 0) - { - _defaultObserverSub = _subject.ObserveOn(_scheduler).Subscribe(_defaultObserver); - } - })); + var inner = _subject.Subscribe(new SchedulingObserver(observer, _scheduler)); + return new Subscription(this, inner); } /// @@ -75,14 +106,40 @@ public IDisposable Subscribe(IObserver observer) /// If we are being called by the IDisposable method. protected virtual void Dispose(bool isDisposing) { - if (isDisposing) + if (!isDisposing) + { + return; + } + + if (_subject is IDisposable disposable) + { + disposable.Dispose(); + } + + _defaultObserverSub.Dispose(); + } + + /// Re-attaches the default observer once the last real subscriber leaves. + private void OnUnsubscribe() + { + if (Interlocked.Decrement(ref _observerRefCount) > 0 || _defaultObserver is null) { - if (_subject is IDisposable disposable) - { - disposable.Dispose(); - } + return; + } - _defaultObserverSub.Dispose(); + _defaultObserverSub = _subject.Subscribe(new SchedulingObserver(_defaultObserver, _scheduler)); + } + + /// Tears down a real subscription and restores the default observer when none remain. + /// The owning subject. + /// The inner subject subscription. + private sealed class Subscription(ScheduledSubject parent, IDisposable inner) : IDisposable + { + /// + public void Dispose() + { + inner.Dispose(); + parent.OnUnsubscribe(); } } } diff --git a/src/ReactiveUI/Scheduler/WaitForDispatcherScheduler.cs b/src/ReactiveUI/Scheduler/WaitForDispatcherScheduler.cs index f5631e37ed..6fe364762c 100644 --- a/src/ReactiveUI/Scheduler/WaitForDispatcherScheduler.cs +++ b/src/ReactiveUI/Scheduler/WaitForDispatcherScheduler.cs @@ -1,8 +1,10 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive.Concurrency; + namespace ReactiveUI; /// @@ -13,7 +15,14 @@ namespace ReactiveUI; /// public class WaitForDispatcherScheduler : IScheduler { + /// + /// Factory function used to create the underlying dispatcher scheduler on demand. + /// private readonly Func _schedulerFactory; + + /// + /// Cached scheduler instance created by the factory, or null if creation has not yet succeeded. + /// private IScheduler? _scheduler; /// @@ -24,10 +33,6 @@ public WaitForDispatcherScheduler(Func schedulerFactory) { _schedulerFactory = schedulerFactory; - // NB: Creating a scheduler will fail on WinRT if we attempt to do - // so on a non-UI thread, even if the underlying Dispatcher exists. - // We assume (hope?) that WaitForDispatcherScheduler will be created - // early enough that this won't be the case. AttemptToCreateScheduler(); } @@ -39,11 +44,18 @@ public IDisposable Schedule(TState state, Func - public IDisposable Schedule(TState state, TimeSpan dueTime, Func action) => // TODO: Create Test + public IDisposable + Schedule( + TState state, + TimeSpan dueTime, + Func action) => AttemptToCreateScheduler().Schedule(state, dueTime, action); /// - public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) => // TODO: Create Test + public IDisposable Schedule( + TState state, + DateTimeOffset dueTime, + Func action) => AttemptToCreateScheduler().Schedule(state, dueTime, action); /// @@ -69,12 +81,10 @@ private IScheduler AttemptToCreateScheduler() } catch (InvalidOperationException) { - // NB: Dispatcher's not ready yet. Keep using CurrentThread return CurrentThreadScheduler.Instance; } catch (ArgumentNullException) { - // NB: Dispatcher's not ready yet. Keep using CurrentThread return CurrentThreadScheduler.Instance; } } diff --git a/src/ReactiveUI/Subjects/IReactiveSubject{T}.cs b/src/ReactiveUI/Subjects/IReactiveSubject{T}.cs new file mode 100644 index 0000000000..eefb21a1d8 --- /dev/null +++ b/src/ReactiveUI/Subjects/IReactiveSubject{T}.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI; + +/// +/// A subject is both an observer and an observable: values pushed in via are broadcast to +/// every current subscriber. This is ReactiveUI's owned replacement for +/// System.Reactive.Subjects.ISubject<T>. +/// +/// The element type pushed through the subject. +public interface IReactiveSubject : IObserver, IObservable; diff --git a/src/ReactiveUI/Suspension/DummySuspensionDriver.cs b/src/ReactiveUI/Suspension/DummySuspensionDriver.cs index 77dc298241..4fca9f220f 100644 --- a/src/ReactiveUI/Suspension/DummySuspensionDriver.cs +++ b/src/ReactiveUI/Suspension/DummySuspensionDriver.cs @@ -1,10 +1,13 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; using System.Text.Json.Serialization.Metadata; +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -24,7 +27,10 @@ public sealed class DummySuspensionDriver : ISuspensionDriver "Implementations commonly use reflection-based serialization. " + "Prefer LoadState(JsonTypeInfo) for trimming or AOT scenarios.")] public IObservable LoadState() - => Observable.Return((object?)null); + => new SingleValueObservable(null); + + /// + public IObservable LoadState(JsonTypeInfo typeInfo) => new SingleValueObservable(default); /// [RequiresUnreferencedCode( @@ -34,21 +40,12 @@ public sealed class DummySuspensionDriver : ISuspensionDriver "Implementations commonly use reflection-based serialization. " + "Prefer SaveState(T, JsonTypeInfo) for trimming or AOT scenarios.")] public IObservable SaveState(T state) - => Observables.Unit; - - /// - public IObservable LoadState(JsonTypeInfo typeInfo) - { - return Observable.Return(default); - } + => SingleValueObservable.Unit; /// - public IObservable SaveState(T state, JsonTypeInfo typeInfo) - { - return Observables.Unit; - } + public IObservable SaveState(T state, JsonTypeInfo typeInfo) => SingleValueObservable.Unit; /// public IObservable InvalidateState() - => Observables.Unit; + => SingleValueObservable.Unit; } diff --git a/src/ReactiveUI/Suspension/SuspensionHost.cs b/src/ReactiveUI/Suspension/SuspensionHost.cs index dd3eb96451..3650f35a7d 100644 --- a/src/ReactiveUI/Suspension/SuspensionHost.cs +++ b/src/ReactiveUI/Suspension/SuspensionHost.cs @@ -1,8 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; + +using ReactiveUI.Internal; + namespace ReactiveUI; /// @@ -13,11 +17,11 @@ namespace ReactiveUI; /// /// backs and provides concrete observables that are wired up /// by helpers such as AutoSuspendHelper. Platform hosts push their lifecycle notifications into the -/// instances exposed here and view models subscribe through . +/// instances exposed here and view models subscribe through . /// /// /// Consumers rarely instantiate this type directly; instead call to access the singleton. The -/// object is intentionally thread-safe via so events raised prior to subscription are +/// object is intentionally thread-safe via so events raised prior to subscription are /// replayed to late subscribers. /// /// @@ -33,16 +37,34 @@ namespace ReactiveUI; /// internal class SuspensionHost : ReactiveObject, ISuspensionHost, IDisposable { - private readonly ReplaySubject> _isLaunchingNew = new(1); + /// + /// Backing subject for IsLaunchingNew. + /// + private readonly ReplayBroadcastSubject> _isLaunchingNew = new(1); - private readonly ReplaySubject> _isResuming = new(1); + /// + /// Backing subject for IsResuming. + /// + private readonly ReplayBroadcastSubject> _isResuming = new(1); - private readonly ReplaySubject> _isUnpausing = new(1); + /// + /// Backing subject for IsUnpausing. + /// + private readonly ReplayBroadcastSubject> _isUnpausing = new(1); - private readonly ReplaySubject> _shouldPersistState = new(1); + /// + /// Backing subject for ShouldPersistState. + /// + private readonly ReplayBroadcastSubject> _shouldPersistState = new(1); - private readonly ReplaySubject> _shouldInvalidateState = new(1); + /// + /// Backing subject for ShouldInvalidateState. + /// + private readonly ReplayBroadcastSubject> _shouldInvalidateState = new(1); + /// + /// Backing store for the AppState property. + /// private object? _appState; /// @@ -51,17 +73,19 @@ internal class SuspensionHost : ReactiveObject, ISuspensionHost, IDisposable public SuspensionHost() { #if COCOA - const string? message = "Your AppDelegate class needs to use AutoSuspendHelper"; + const string? Message = "Your AppDelegate class needs to use AutoSuspendHelper"; #elif ANDROID - const string? message = "You need to create an App class and use AutoSuspendHelper"; + const string? Message = "You need to create an App class and use AutoSuspendHelper"; #else - const string? message = "Your App class needs to use AutoSuspendHelper"; + const string? Message = "Your App class needs to use AutoSuspendHelper"; #endif - IsLaunchingNew = IsResuming = IsUnpausing = ShouldInvalidateState = - Observable.Throw(new Exception(message)); + ShouldInvalidateState = new ThrowObservable(new InvalidOperationException(Message)); + IsUnpausing = ShouldInvalidateState; + IsResuming = IsUnpausing; + IsLaunchingNew = IsResuming; - ShouldPersistState = Observable.Throw(new Exception(message)); + ShouldPersistState = new ThrowObservable(new InvalidOperationException(Message)); } /// @@ -71,9 +95,9 @@ public SuspensionHost() /// Raised when the host platform reports that the previous process image is being restored (for example, when an /// Android Activity is recreated with a saved bundle). Use this signal to reload persisted state before showing UI. /// - public IObservable IsResuming // TODO: Create Test + public IObservable IsResuming { - get => _isResuming.Switch(); + get => new WhenAnyObservableSwitchSink(_isResuming); set => _isResuming.OnNext(value); } @@ -84,9 +108,9 @@ public SuspensionHost() /// Fired when the app returns to the foreground without being recreated (for example, when an Activity is resumed /// after being paused). React to this stream to refresh transient UI state that should not be serialized. /// - public IObservable IsUnpausing // TODO: Create Test + public IObservable IsUnpausing { - get => _isUnpausing.Switch(); + get => new WhenAnyObservableSwitchSink(_isUnpausing); set => _isUnpausing.OnNext(value); } @@ -97,9 +121,9 @@ public SuspensionHost() /// Subscribers should write to durable storage and dispose the provided token once the /// operation completes so platform helpers can release any background execution grants. /// - public IObservable ShouldPersistState // TODO: Create Test + public IObservable ShouldPersistState { - get => _shouldPersistState.Switch(); + get => new WhenAnyObservableSwitchSink(_shouldPersistState); set => _shouldPersistState.OnNext(value); } @@ -110,9 +134,9 @@ public SuspensionHost() /// Emits when the platform indicates a clean launch (for example, no saved Android bundle). Use this to create /// default state via or to initialize services only needed on cold start. /// - public IObservable IsLaunchingNew // TODO: Create Test + public IObservable IsLaunchingNew { - get => _isLaunchingNew.Switch(); + get => new WhenAnyObservableSwitchSink(_isLaunchingNew); set => _isLaunchingNew.OnNext(value); } @@ -123,9 +147,9 @@ public SuspensionHost() /// Triggered when the host detects an unrecoverable failure (for example, AppDomain unhandled exceptions). Use it to /// delete corrupt state and log crash telemetry before the process terminates. /// - public IObservable ShouldInvalidateState // TODO: Create Test + public IObservable ShouldInvalidateState { - get => _shouldInvalidateState.Switch(); + get => new WhenAnyObservableSwitchSink(_shouldInvalidateState); set => _shouldInvalidateState.OnNext(value); } @@ -172,13 +196,15 @@ public void Dispose() /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { - if (disposing) + if (!disposing) { - _isLaunchingNew.Dispose(); - _isResuming.Dispose(); - _isUnpausing.Dispose(); - _shouldPersistState.Dispose(); - _shouldInvalidateState.Dispose(); + return; } + + _isLaunchingNew.Dispose(); + _isResuming.Dispose(); + _isUnpausing.Dispose(); + _shouldPersistState.Dispose(); + _shouldInvalidateState.Dispose(); } } diff --git a/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs b/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs index 039c4439c7..cb39b0a832 100644 --- a/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs +++ b/src/ReactiveUI/Suspension/SuspensionHostExtensions.cs @@ -1,11 +1,14 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; +using System.Reactive.Disposables; +using System.Reactive.Linq; using System.Text.Json.Serialization.Metadata; - using ReactiveUI.Interfaces; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -34,7 +37,14 @@ public static class SuspensionHostExtensions /// /// Gets or sets the ensure load app state function. Internal for testing purposes only. /// - [SuppressMessage("Roslynator", "RCS1085:Use auto-implemented property", Justification = "Need explicit backing field for Interlocked.Exchange")] + [SuppressMessage( + "Minor Code Smell", + "S2292:Trivial properties should be auto-implemented", + Justification = "Backing field required for Interlocked.Exchange(ref).")] + [SuppressMessage( + "Roslynator", + "RCS1085:Use auto-implemented property", + Justification = "Need explicit backing field for Interlocked.Exchange")] internal static Func>? EnsureLoadAppStateFunc { get => _ensureLoadAppStateFunc; @@ -44,7 +54,14 @@ internal static Func>? EnsureLoadAppStateFunc /// /// Gets or sets the suspension driver. Internal for testing purposes only. /// - [SuppressMessage("Roslynator", "RCS1085:Use auto-implemented property", Justification = "Need explicit backing field for Interlocked.Exchange")] + [SuppressMessage( + "Minor Code Smell", + "S2292:Trivial properties should be auto-implemented", + Justification = "Backing field required for Interlocked.Exchange(ref).")] + [SuppressMessage( + "Roslynator", + "RCS1085:Use auto-implemented property", + Justification = "Need explicit backing field for Interlocked.Exchange")] internal static ISuspensionDriver? SuspensionDriver { get => _suspensionDriver; @@ -67,6 +84,10 @@ internal static ISuspensionDriver? SuspensionDriver [RequiresDynamicCode( "This overload may invoke ISuspensionDriver.LoadState(), which is commonly reflection-based. " + "Prefer GetAppState(ISuspensionHost) used with SetupDefaultSuspendResume(..., JsonTypeInfo, ...) for trimming/AOT scenarios.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static T GetAppState(this ISuspensionHost item) { ArgumentExceptionHelper.ThrowIfNull(item); @@ -112,14 +133,18 @@ public static TAppState GetAppState(this ISuspensionHost i [RequiresDynamicCode( "This overload uses WhenAny, which can require unreferenced/dynamic code in trimming/AOT scenarios. " + "Prefer ObserveAppState(ISuspensionHost) for trimming/AOT scenarios.")] + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] public static IObservable ObserveAppState(this ISuspensionHost item) where T : class { ArgumentExceptionHelper.ThrowIfNull(item); - return item.WhenAny(nameof(item.AppState), static observedChange => observedChange.Value) - .WhereNotNull() - .Cast(); + return new NonNullCastObservable(item.WhenAny( + nameof(item.AppState), + static observedChange => observedChange.Value)); } /// @@ -136,21 +161,23 @@ public static IObservable ObserveAppState(this ISuspension { ArgumentExceptionHelper.ThrowIfNull(item); - return Observable.Create( - observer => - { - var current = item.AppStateValue; - if (current is not null) - { - observer.OnNext(current); - } - - return item.AppStateValueChanged - .WhereNotNull() - .Subscribe(observer); - }); + return new AppStateValueObservable(item); } + /// + /// Setup our suspension driver for a class derived off ISuspensionHost interface using a resolved driver. + /// + /// The suspension host. + /// A disposable which will stop responding to Suspend and Resume requests. + [RequiresUnreferencedCode( + "This overload may invoke ISuspensionDriver.LoadState()/SaveState(T), which are commonly reflection-based. " + + "Prefer SetupDefaultSuspendResume(..., JsonTypeInfo, ...) for trimming/AOT scenarios.")] + [RequiresDynamicCode( + "This overload may invoke ISuspensionDriver.LoadState()/SaveState(T), which are commonly reflection-based. " + + "Prefer SetupDefaultSuspendResume(..., JsonTypeInfo, ...) for trimming/AOT scenarios.")] + public static IDisposable SetupDefaultSuspendResume(this ISuspensionHost item) => + SetupDefaultSuspendResume(item, null); + /// /// Setup our suspension driver for a class derived off ISuspensionHost interface. /// This will make your suspension host respond to suspend and resume requests. @@ -160,9 +187,9 @@ public static IObservable ObserveAppState(this ISuspension /// A disposable which will stop responding to Suspend and Resume requests. /// /// - /// Registers handlers for , , - /// and resume notifications, delegating serialization to the provided (or a resolved - /// instance from ). + /// Registers handlers for ISuspensionHost.ShouldPersistState, ISuspensionHost.ShouldInvalidateState, + /// and resume notifications, delegating serialization to the provided driver (or a resolved + /// instance from AppLocator). /// /// /// @@ -179,84 +206,116 @@ public static IObservable ObserveAppState(this ISuspension [RequiresDynamicCode( "This overload may invoke ISuspensionDriver.LoadState()/SaveState(T), which are commonly reflection-based. " + "Prefer SetupDefaultSuspendResume(..., JsonTypeInfo, ...) for trimming/AOT scenarios.")] - public static IDisposable SetupDefaultSuspendResume(this ISuspensionHost item, ISuspensionDriver? driver = null) + public static IDisposable SetupDefaultSuspendResume(this ISuspensionHost item, ISuspensionDriver? driver) { ArgumentExceptionHelper.ThrowIfNull(item); - var ret = new CompositeDisposable(); + CompositeDisposable ret = []; _suspensionDriver ??= driver ?? AppLocator.Current.GetService(); if (_suspensionDriver is null) { item.Log().Error("Could not find a valid driver and therefore cannot setup Suspend/Resume."); - return Disposable.Empty; + return EmptyDisposable.Instance; } _ensureLoadAppStateFunc = () => EnsureLoadAppState(item, _suspensionDriver); - ret.Add(item.ShouldInvalidateState - .SelectMany(_ => _suspensionDriver.InvalidateState()) - .LoggedCatch(item, Observables.Unit, "Tried to invalidate app state") - .Subscribe(_ => item.Log().Info("Invalidated app state"))); - - ret.Add(item.ShouldPersistState - .SelectMany(x => EnsureLoadAppStateOnce(item, _suspensionDriver) - .SelectMany(_ => _suspensionDriver!.SaveState(item.AppState!)) - .Finally(x.Dispose)) - .LoggedCatch(item, Observables.Unit, "Tried to persist app state") - .Subscribe(_ => item.Log().Info("Persisted application state"))); - - ret.Add(item.IsResuming.Merge(item.IsLaunchingNew) - .Do(_ => Interlocked.Exchange(ref _ensureLoadAppStateFunc, null)?.Invoke()) - .Subscribe()); + var resolvedDriver = _suspensionDriver!; + ret.Add(item.ShouldInvalidateState.Subscribe(new DriverOperationObserver( + item, + _ => resolvedDriver.InvalidateState(), + static _ => { }, + "Invalidated app state", + "Tried to invalidate app state"))); + + ret.Add(item.ShouldPersistState.Subscribe(new DriverOperationObserver( + item, + _ => + { + // Materialize app state (one-time load) before saving, so a shutdown that races ahead of + // the resume/launch load still persists real state rather than null (see #4353). + RunPendingLoad(); + return resolvedDriver.SaveState(item.AppState!); + }, + static token => token.Dispose(), + "Persisted application state", + "Tried to persist app state"))); + + ret.Add(item.IsResuming.Subscribe(new DelegateObserver(static _ => RunPendingLoad()))); + ret.Add(item.IsLaunchingNew.Subscribe(new DelegateObserver(static _ => RunPendingLoad()))); return ret; } + /// + /// Sets up suspend/resume using a strongly-typed host and source-generated JSON metadata, using a resolved driver. + /// + /// The application state type. + /// The typed suspension host. + /// Source-generated metadata for TAppState. + /// A disposable which will stop responding to Suspend and Resume requests. + public static IDisposable SetupDefaultSuspendResume( + this ISuspensionHost item, + JsonTypeInfo typeInfo) + where TAppState : class => + SetupDefaultSuspendResume(item, typeInfo, null); + /// /// Sets up suspend/resume using a strongly-typed host and source-generated JSON metadata (trimming/AOT friendly). /// /// The application state type. /// The typed suspension host. - /// Source-generated metadata for . + /// Source-generated metadata for TAppState. /// The suspension driver. /// A disposable which will stop responding to Suspend and Resume requests. /// - /// This overload persists and restores state using and - /// to avoid reflection-based serialization. + /// This overload persists and restores state using ISuspensionDriver.LoadState and ISuspensionDriver.SaveState + /// to avoid reflection-based serialization. /// - public static IDisposable SetupDefaultSuspendResume(this ISuspensionHost item, JsonTypeInfo typeInfo, ISuspensionDriver? driver = null) + public static IDisposable SetupDefaultSuspendResume( + this ISuspensionHost item, + JsonTypeInfo typeInfo, + ISuspensionDriver? driver) where TAppState : class { ArgumentExceptionHelper.ThrowIfNull(item); ArgumentExceptionHelper.ThrowIfNull(typeInfo); - var ret = new CompositeDisposable(); + CompositeDisposable ret = []; _suspensionDriver ??= driver ?? AppLocator.Current.GetService(); if (_suspensionDriver is null) { item.Log().Error("Could not find a valid driver and therefore cannot setup Suspend/Resume."); - return Disposable.Empty; + return EmptyDisposable.Instance; } _ensureLoadAppStateFunc = () => EnsureLoadAppState(item, _suspensionDriver, typeInfo); - ret.Add(item.ShouldInvalidateState - .SelectMany(_ => _suspensionDriver.InvalidateState()) - .LoggedCatch(item, Observables.Unit, "Tried to invalidate app state") - .Subscribe(_ => item.Log().Info("Invalidated app state"))); - - ret.Add(item.ShouldPersistState - .SelectMany(x => EnsureLoadAppStateOnce(item, _suspensionDriver, typeInfo) - .SelectMany(_ => _suspensionDriver!.SaveState(item.AppStateValue!, typeInfo)) - .Finally(x.Dispose)) - .LoggedCatch(item, Observables.Unit, "Tried to persist app state") - .Subscribe(_ => item.Log().Info("Persisted application state"))); - - ret.Add(item.IsResuming.Merge(item.IsLaunchingNew) - .Do(_ => Interlocked.Exchange(ref _ensureLoadAppStateFunc, null)?.Invoke()) - .Subscribe()); + var resolvedDriver = _suspensionDriver!; + ret.Add(item.ShouldInvalidateState.Subscribe(new DriverOperationObserver( + item, + _ => resolvedDriver.InvalidateState(), + static _ => { }, + "Invalidated app state", + "Tried to invalidate app state"))); + + ret.Add(item.ShouldPersistState.Subscribe(new DriverOperationObserver( + item, + _ => + { + // Materialize app state (one-time load) before saving, so a shutdown that races ahead of + // the resume/launch load still persists real state rather than null (see #4353). + RunPendingLoad(); + return resolvedDriver.SaveState(item.AppStateValue!, typeInfo); + }, + static token => token.Dispose(), + "Persisted application state", + "Tried to persist app state"))); + + ret.Add(item.IsResuming.Subscribe(new DelegateObserver(static _ => RunPendingLoad()))); + ret.Add(item.IsLaunchingNew.Subscribe(new DelegateObserver(static _ => RunPendingLoad()))); return ret; } @@ -277,7 +336,7 @@ private static IObservable EnsureLoadAppState(this ISuspensionHost item, I { if (item.AppState is not null) { - return Observable.Return(Unit.Default); + return Observable.Default; } _suspensionDriver ??= driver ?? AppLocator.Current.GetService(); @@ -285,43 +344,21 @@ private static IObservable EnsureLoadAppState(this ISuspensionHost item, I if (_suspensionDriver is null) { item.Log().Error("Could not find a valid driver and therefore cannot load app state."); - return Observable.Return(Unit.Default); + return Observable.Default; } - object? loadedState; - try { - loadedState = _suspensionDriver.LoadState().Wait(); + // Fall back to a freshly created state when the driver yields no persisted state (see #4349). + item.AppState = WaitForResult(_suspensionDriver.LoadState()) ?? item.CreateNewAppState?.Invoke(); } catch (Exception ex) { item.Log().Warn(ex, "Failed to restore app state from storage, creating from scratch"); - loadedState = null; + item.AppState = item.CreateNewAppState?.Invoke(); } - item.AppState = loadedState ?? item.CreateNewAppState?.Invoke(); - - return Observable.Return(Unit.Default); - } - - /// - /// Runs the pending one-time untyped app-state load, or materializes state directly if the pending loader has already been consumed. - /// - /// The suspension host. - /// The suspension driver. - /// A completed observable. - [RequiresUnreferencedCode( - "This overload may invoke ISuspensionDriver.LoadState(), which is commonly reflection-based. " + - "Prefer EnsureLoadAppStateOnce(ISuspensionHost, ISuspensionDriver?, JsonTypeInfo) for trimming/AOT scenarios.")] - [RequiresDynamicCode( - "This overload may invoke ISuspensionDriver.LoadState(), which is commonly reflection-based. " + - "Prefer EnsureLoadAppStateOnce(ISuspensionHost, ISuspensionDriver?, JsonTypeInfo) for trimming/AOT scenarios.")] - private static IObservable EnsureLoadAppStateOnce(ISuspensionHost item, ISuspensionDriver? driver) - { - var ensureLoadAppState = Interlocked.Exchange(ref _ensureLoadAppStateFunc, null); - - return ensureLoadAppState?.Invoke() ?? item.EnsureLoadAppState(driver); + return Observable.Default; } /// @@ -332,12 +369,15 @@ private static IObservable EnsureLoadAppStateOnce(ISuspensionHost item, IS /// The suspension driver. /// Source-generated metadata for . /// A completed observable. - private static IObservable EnsureLoadAppState(this ISuspensionHost item, ISuspensionDriver? driver, JsonTypeInfo typeInfo) + private static IObservable EnsureLoadAppState( + this ISuspensionHost item, + ISuspensionDriver? driver, + JsonTypeInfo typeInfo) where TAppState : class { if (item.AppStateValue is not null) { - return Observable.Return(Unit.Default); + return Observable.Default; } _suspensionDriver ??= driver ?? AppLocator.Current.GetService(); @@ -345,39 +385,240 @@ private static IObservable EnsureLoadAppState(this ISuspensionH if (_suspensionDriver is null) { item.Log().Error("Could not find a valid driver and therefore cannot load app state."); - return Observable.Return(Unit.Default); + return Observable.Default; } - TAppState? loadedState; - try { - loadedState = _suspensionDriver.LoadState(typeInfo).Wait(); + // Fall back to a freshly created state when the driver yields no persisted state (see #4349). + item.AppStateValue = WaitForResult(_suspensionDriver.LoadState(typeInfo)) ?? item.CreateNewAppStateTyped?.Invoke(); } catch (Exception ex) { item.Log().Warn(ex, "Failed to restore app state from storage, creating from scratch"); - loadedState = null; + item.AppStateValue = item.CreateNewAppStateTyped?.Invoke(); } - item.AppStateValue = loadedState ?? item.CreateNewAppStateTyped?.Invoke(); + return Observable.Default; + } + + /// Runs the pending one-time app-state load exactly once, if one is registered. + private static void RunPendingLoad() => Interlocked.Exchange(ref _ensureLoadAppStateFunc, null)?.Invoke(); + + /// Subscribes to a single-value observable and blocks until it terminates, returning its last value. + /// The value type. + /// The source observable. + /// The last value produced by the source, or default if none was produced. + private static T? WaitForResult(IObservable source) + { + using var signal = new ManualResetEventSlim(false); + var observer = new BlockingObserver(signal); + using (source.Subscribe(observer)) + { + signal.Wait(); + } - return Observable.Return(Unit.Default); + return observer.GetResult(); } /// - /// Runs the pending one-time typed app-state load, or materializes state directly if the pending loader has already been consumed. + /// Emits only the non-null values of a WhenAny stream, cast to the requested type. Specialised to + /// . + /// + /// The requested app-state type. + /// The source app-state value stream. + private sealed class NonNullCastObservable(IObservable source) : IObservable + where TResult : class + { + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + return source.Subscribe(new Sink(observer)); + } + + /// Forwards each non-null value cast to the requested type. + /// The observer receiving the cast values. + private sealed class Sink(IObserver downstream) : IObserver + { + /// + public void OnNext(object? value) + { + if (value is not TResult typed) + { + return; + } + + downstream.OnNext(typed); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// + /// Emits the current typed app state (when present) followed by every non-null assignment. Specialised to + /// . /// /// The application state type. /// The typed suspension host. - /// The suspension driver. - /// Source-generated metadata for . - /// A completed observable. - private static IObservable EnsureLoadAppStateOnce(ISuspensionHost item, ISuspensionDriver? driver, JsonTypeInfo typeInfo) + private sealed class AppStateValueObservable(ISuspensionHost item) : IObservable where TAppState : class { - var ensureLoadAppState = Interlocked.Exchange(ref _ensureLoadAppStateFunc, null); + /// + public IDisposable Subscribe(IObserver observer) + { + ArgumentExceptionHelper.ThrowIfNull(observer); + + var current = item.AppStateValue; + if (current is not null) + { + observer.OnNext(current); + } + + return item.AppStateValueChanged.Subscribe(new Sink(observer)); + } + + /// Forwards each non-null app-state assignment. + /// The observer receiving the app state. + private sealed class Sink(IObserver downstream) : IObserver + { + /// + public void OnNext(TAppState? value) + { + if (value is null) + { + return; + } + + downstream.OnNext(value); + } + + /// + public void OnError(Exception error) => downstream.OnError(error); + + /// + public void OnCompleted() => downstream.OnCompleted(); + } + } + + /// + /// For each lifecycle signal, runs a suspension-driver operation, logging success and swallowing/logging failures, + /// and runs a finalizer afterwards. Fuses the prior SelectMany + Finally + LoggedCatch + + /// Subscribe pipeline into one observer. + /// + /// The lifecycle signal type. + /// The object used for logging. + /// Produces the driver operation for a signal. + /// Runs after the operation terminates (for example, disposing the persist token). + /// Logged when the operation succeeds. + /// Logged when the operation (or its creation) fails. + private sealed class DriverOperationObserver( + IEnableLogger logHost, + Func> operation, + Action onFinally, + string successMessage, + string errorMessage) : IObserver + { + /// + public void OnNext(TArg value) + { + IObservable op; + try + { + op = operation(value); + } + catch (Exception ex) + { + logHost.Log().Warn(ex, errorMessage); + onFinally(value); + return; + } + + op.Subscribe(new ResultObserver(logHost, value, onFinally, successMessage, errorMessage)); + } + + /// + public void OnError(Exception error) => logHost.Log().Warn(error, errorMessage); + + /// + public void OnCompleted() + { + } + + /// Logs the result of a single driver operation and runs the finalizer exactly once. + /// The object used for logging. + /// The signal the operation was run for. + /// Runs after the operation terminates. + /// Logged when the operation succeeds. + /// Logged when the operation fails. + private sealed class ResultObserver( + IEnableLogger logHost, + TArg arg, + Action onFinally, + string successMessage, + string errorMessage) : IObserver + { + /// Whether the finalizer has run. + private bool _finished; + + /// + public void OnNext(Unit value) => logHost.Log().Info(successMessage); + + /// + public void OnError(Exception error) + { + logHost.Log().Warn(error, errorMessage); + Finish(); + } + + /// + public void OnCompleted() => Finish(); + + /// Runs the finalizer exactly once. + private void Finish() + { + if (_finished) + { + return; + } + + _finished = true; + onFinally(arg); + } + } + } + + /// Captures the last value (or terminal error) of a blocking subscription and signals on termination. + /// The value type. + /// Set when the source terminates. + private sealed class BlockingObserver(ManualResetEventSlim signal) : IObserver + { + /// The last value produced by the source. + private T? _value; + + /// The terminal error, if the source errored. + private Exception? _error; + + /// + public void OnNext(T value) => _value = value; + + /// + public void OnError(Exception error) + { + _error = error; + signal.Set(); + } + + /// + public void OnCompleted() => signal.Set(); - return ensureLoadAppState?.Invoke() ?? item.EnsureLoadAppState(driver, typeInfo); + /// Returns the captured value, rethrowing any terminal error. + /// The last value produced by the source. + public T? GetResult() => _error is not null ? throw _error : _value; } } diff --git a/src/ReactiveUI/Suspension/SuspensionHost{TAppState}.cs b/src/ReactiveUI/Suspension/SuspensionHost{TAppState}.cs index 50242e5dd8..859c27778e 100644 --- a/src/ReactiveUI/Suspension/SuspensionHost{TAppState}.cs +++ b/src/ReactiveUI/Suspension/SuspensionHost{TAppState}.cs @@ -1,9 +1,11 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System.Reactive; using ReactiveUI.Interfaces; +using ReactiveUI.Internal; namespace ReactiveUI; @@ -30,37 +32,37 @@ public class SuspensionHost : ReactiveObject, ISuspensionHost /// Holds the observable that signals when the application is launching new. /// - private readonly ReplaySubject> _isLaunchingNew = new(1); + private readonly ReplayBroadcastSubject> _isLaunchingNew = new(1); /// /// Holds the observable that signals when the application is resuming from a suspended state. /// - private readonly ReplaySubject> _isResuming = new(1); + private readonly ReplayBroadcastSubject> _isResuming = new(1); /// /// Holds the observable that signals when the application is activated / unpausing. /// - private readonly ReplaySubject> _isUnpausing = new(1); + private readonly ReplayBroadcastSubject> _isUnpausing = new(1); /// /// Holds the observable that signals when the application should persist its state. /// - private readonly ReplaySubject> _shouldPersistState = new(1); + private readonly ReplayBroadcastSubject> _shouldPersistState = new(1); /// /// Holds the observable that signals when persisted state should be invalidated. /// - private readonly ReplaySubject> _shouldInvalidateState = new(1); + private readonly ReplayBroadcastSubject> _shouldInvalidateState = new(1); /// /// Holds the observable that signals when the application is continuing from a temporarily paused state. /// - private readonly ReplaySubject> _isContinuing = new(1); + private readonly ReplayBroadcastSubject> _isContinuing = new(1); /// /// Publishes changes to when assigned. /// - private readonly Subject _appStateValueChanged = new(); + private readonly BroadcastSubject _appStateValueChanged = new(); /// /// Stores the typed application state factory. @@ -82,17 +84,20 @@ public class SuspensionHost : ReactiveObject, ISuspensionHost(new Exception(message)); + ShouldInvalidateState = new ThrowObservable(new InvalidOperationException(Message)); + IsContinuing = ShouldInvalidateState; + IsUnpausing = IsContinuing; + IsResuming = IsUnpausing; + IsLaunchingNew = IsResuming; - ShouldPersistState = Observable.Throw(new Exception(message)); + ShouldPersistState = new ThrowObservable(new InvalidOperationException(Message)); } /// @@ -104,7 +109,7 @@ public SuspensionHost() /// Thrown when the value is . public IObservable IsLaunchingNew { - get => _isLaunchingNew.Switch(); + get => new WhenAnyObservableSwitchSink(_isLaunchingNew); set { ArgumentExceptionHelper.ThrowIfNull(value); @@ -121,7 +126,7 @@ public IObservable IsLaunchingNew /// Thrown when the value is . public IObservable IsResuming { - get => _isResuming.Switch(); + get => new WhenAnyObservableSwitchSink(_isResuming); set { ArgumentExceptionHelper.ThrowIfNull(value); @@ -138,7 +143,7 @@ public IObservable IsResuming /// Thrown when the value is . public IObservable IsUnpausing { - get => _isUnpausing.Switch(); + get => new WhenAnyObservableSwitchSink(_isUnpausing); set { ArgumentExceptionHelper.ThrowIfNull(value); @@ -156,7 +161,7 @@ public IObservable IsUnpausing /// Thrown when the value is . public IObservable IsContinuing { - get => _isContinuing.Switch(); + get => new WhenAnyObservableSwitchSink(_isContinuing); set { ArgumentExceptionHelper.ThrowIfNull(value); @@ -173,7 +178,7 @@ public IObservable IsContinuing /// Thrown when the value is . public IObservable ShouldPersistState { - get => _shouldPersistState.Switch(); + get => new WhenAnyObservableSwitchSink(_shouldPersistState); set { ArgumentExceptionHelper.ThrowIfNull(value); @@ -190,7 +195,7 @@ public IObservable ShouldPersistState /// Thrown when the value is . public IObservable ShouldInvalidateState { - get => _shouldInvalidateState.Switch(); + get => new WhenAnyObservableSwitchSink(_shouldInvalidateState); set { ArgumentExceptionHelper.ThrowIfNull(value); @@ -222,10 +227,8 @@ public TAppState? AppStateValue get => _appState; set { - // Keep ReactiveObject semantics for existing consumers. this.RaiseAndSetIfChanged(ref _appState, value); - // Publish change notification for trimming/AOT-friendly observation. _appStateValueChanged.OnNext(value); } } @@ -252,9 +255,7 @@ public TAppState? AppStateValue get { var typedFactory = _createNewAppStateTyped; - Func? returnFunc = typedFactory is null ? null : () => typedFactory.Invoke()!; - - return returnFunc; + return typedFactory is null ? null : () => typedFactory.Invoke()!; } set @@ -270,7 +271,8 @@ public TAppState? AppStateValue var created = value(); return created is TAppState typed ? typed - : throw new InvalidCastException($"Created app state is not assignable to {typeof(TAppState).FullName}."); + : throw new InvalidCastException( + $"Created app state is not assignable to {typeof(TAppState).FullName}."); }; } } diff --git a/src/ReactiveUI/Temp/CompositeDisposable.cs b/src/ReactiveUI/Temp/CompositeDisposable.cs new file mode 100644 index 0000000000..7c9b186d53 --- /dev/null +++ b/src/ReactiveUI/Temp/CompositeDisposable.cs @@ -0,0 +1,585 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. +// + +#nullable enable + +using System.Collections; + +namespace System.Reactive.Disposables; + +/// +/// Represents a group of disposable resources that are disposed together. +/// +[SuppressMessage( + "Microsoft.Naming", + "CA1710:IdentifiersShouldHaveCorrectSuffix", + Justification = "Backward compat + ideally want to get rid of the ICollection nature of the type.")] +public sealed class CompositeDisposable : ICollection, ICancelable +{ + #if NET9_0_OR_GREATER + private readonly Lock _gate = new(); + #else + private readonly object _gate = new(); + #endif + private bool _disposed; + private object _disposables; + private int _count; + private const int ShrinkThreshold = 64; + + // The maximum number of items to keep in a list before switching to a dictionary. + // Issue https://github.com/dotnet/reactive/issues/2005 reported that when a SelectMany + // observes large numbers (1000s) of observables, the CompositeDisposable it uses to + // keep track of all of the inner observables it creates becomes a bottleneck when the + // subscription completes. + private const int MaximumLinearSearchThreshold = 1024; + + // Default initial capacity of the _disposables list in case + // The number of items is not known upfront + private const int DefaultCapacity = 16; + + /// + /// Initializes a new instance of the class with no disposables contained by it initially. + /// + public CompositeDisposable() + { + _disposables = new List(); + } + + /// + /// Initializes a new instance of the class with the specified number of disposables. + /// + /// The number of disposables that the new CompositeDisposable can initially store. + /// is less than zero. + public CompositeDisposable(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity)); + } + + _disposables = new List(capacity); + } + + /// + /// Initializes a new instance of the class from a group of disposables. + /// + /// Disposables that will be disposed together. + /// is null. + /// Any of the disposables in the collection is null. + public CompositeDisposable(params IDisposable[] disposables) + { + if (disposables == null) + { + throw new ArgumentNullException(nameof(disposables)); + } + + (_disposables, _) = ToListOrDictionary(disposables); + + // _count can be read by other threads and thus should be properly visible + // also releases the _disposables contents so it becomes thread-safe + Volatile.Write(ref _count, disposables.Length); + } + + /// + /// Initializes a new instance of the class from a group of disposables. + /// + /// Disposables that will be disposed together. + /// is null. + /// Any of the disposables in the collection is null. + public CompositeDisposable(IEnumerable disposables) + { + if (disposables == null) + { + throw new ArgumentNullException(nameof(disposables)); + } + + (_disposables, var count) = ToListOrDictionary(disposables); + + // _count can be read by other threads and thus should be properly visible + // also releases the _disposables contents so it becomes thread-safe + Volatile.Write(ref _count, count); + } + + private static (object Collection, int Count) ToListOrDictionary(IEnumerable disposables) + { + var capacity = disposables switch + { + IDisposable[] a => a.Length, + ICollection c => c.Count, + _ => DefaultCapacity + }; + + if (capacity > MaximumLinearSearchThreshold) + { + var dictionary = new Dictionary(capacity); + var disposableCount = 0; + foreach (var d in disposables) + { + if (d == null) + { + throw new ArgumentException("disposables", nameof(disposables)); + } + + dictionary.TryGetValue(d, out var thisDisposableCount); + dictionary[d] = thisDisposableCount + 1; + + disposableCount += 1; + } + + return (dictionary, disposableCount); + } + + var list = new List(capacity); + + // do the copy and null-check in one step to avoid a + // second loop for just checking for null items + foreach (var d in disposables) + { + if (d == null) + { + throw new ArgumentException("disposables", nameof(disposables)); + } + + list.Add(d); + } + + if (list.Count > MaximumLinearSearchThreshold) + { + // We end up here if we didn't know the count up front because it's an + // IEnumerable and not an ICollection, and it then turns out that + // the number of items exceeds our maximum tolerance for linear search. + } + + return (list, list.Count); + } + + /// + /// Gets the number of disposables contained in the . + /// + public int Count => Volatile.Read(ref _count); + + /// + /// Adds a disposable to the or disposes the disposable if the is disposed. + /// + /// Disposable to add. + /// is null. + public void Add(IDisposable item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + lock (_gate) + { + if (!_disposed) + { + if (_disposables is List listDisposables) + { + listDisposables.Add(item); + + // Once we get to thousands of items (which happens with wide fan-out/in configurations) + // the cost of linear search becomes too high. We switch to a dictionary at that point. + // See https://github.com/dotnet/reactive/issues/2005 + if (listDisposables.Count > MaximumLinearSearchThreshold) + { + // If we've blown through this threshold, chances are there's more to come, + // so allocate some more spare capacity. + var dictionary = + new Dictionary(listDisposables.Count + (listDisposables.Count / 4)); + foreach (var d in listDisposables) + { + if (d is not null) + { + dictionary.TryGetValue(d, out var thisDisposableCount); + dictionary[d] = thisDisposableCount + 1; + } + } + + _disposables = dictionary; + } + } + else + { + var dictionaryDisposables = (Dictionary)_disposables; + dictionaryDisposables.TryGetValue(item, out var thisDisposableCount); + dictionaryDisposables[item] = thisDisposableCount + 1; + } + + // If read atomically outside the lock, it should be written atomically inside + // the plain read on _count is fine here because manipulation always happens + // from inside a lock. + Volatile.Write(ref _count, _count + 1); + return; + } + } + + item.Dispose(); + } + + /// + /// Removes and disposes the first occurrence of a disposable from the . + /// + /// Disposable to remove. + /// true if found; false otherwise. + /// is null. + public bool Remove(IDisposable item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + lock (_gate) + { + // this composite was already disposed and if the item was in there + // it has been already removed/disposed + if (_disposed) + { + return false; + } + + // + // List doesn't shrink the size of the underlying array but does collapse the array + // by copying the tail one position to the left of the removal index. We don't need + // index-based lookup but only ordering for sequential disposal. So, instead of spending + // cycles on the Array.Copy imposed by Remove, we use a null sentinel value. We also + // do manual Swiss cheese detection to shrink the list if there's a lot of holes in it. + // + + // read fields as infrequently as possible + var current = _disposables; + + if (current is List currentList) + { + var i = currentList.IndexOf(item); + if (i < 0) + { + // not found, just return + return false; + } + + currentList[i] = null; + + if (currentList.Capacity > ShrinkThreshold && _count < currentList.Capacity / 2) + { + var fresh = new List(currentList.Capacity / 2); + + foreach (var d in currentList) + { + if (d != null) + { + fresh.Add(d); + } + } + + _disposables = fresh; + } + } + else + { + var dictionaryDisposables = (Dictionary)_disposables; + if (!dictionaryDisposables.TryGetValue(item, out var thisDisposableCount)) + { + return false; + } + + thisDisposableCount -= 1; + if (thisDisposableCount == 0) + { + dictionaryDisposables.Remove(item); + } + else + { + dictionaryDisposables[item] = thisDisposableCount; + } + } + + // make sure the Count property sees an atomic update + Volatile.Write(ref _count, _count - 1); + } + + // if we get here, the item was found and removed from the list + // just dispose it and report success + + item.Dispose(); + + return true; + } + + /// + /// Disposes all disposables in the group and removes them from the group. + /// + public void Dispose() + { + List? currentDisposablesList = null; + Dictionary? currentDisposablesDictionary = null; + + lock (_gate) + { + if (!_disposed) + { + currentDisposablesList = _disposables as List; + currentDisposablesDictionary = _disposables as Dictionary; + + // nulling out the reference is faster no risk to + // future Add/Remove because _disposed will be true + // and thus _disposables won't be touched again. + _disposables = null!; // NB: All accesses are guarded by _disposed checks. + + Volatile.Write(ref _count, 0); + Volatile.Write(ref _disposed, true); + } + } + + if (currentDisposablesList is not null) + { + foreach (var d in currentDisposablesList) + { + // Although we don't all nulls in from the outside, we implement Remove + // by setting entries to null, and shrinking the list if it gets too sparse. + // So some entries may be null. + d?.Dispose(); + } + } + + if (currentDisposablesDictionary is not null) + { + foreach (var kv in currentDisposablesDictionary) + { + kv.Key.Dispose(); + } + } + } + + /// + /// Removes and disposes all disposables from the , but does not dispose the . + /// + public void Clear() + { + IDisposable?[] previousDisposables; + + lock (_gate) + { + // disposed composites are always clear + if (_disposed) + { + return; + } + + var current = _disposables; + + if (current is List currentList) + { + previousDisposables = currentList.ToArray(); + currentList.Clear(); + } + else + { + var currentDictionary = (Dictionary)current; + previousDisposables = new IDisposable[currentDictionary.Count]; + currentDictionary.Keys.CopyTo(previousDisposables!, 0); + currentDictionary.Clear(); + } + + Volatile.Write(ref _count, 0); + } + + foreach (var d in previousDisposables) + { + d?.Dispose(); + } + } + + /// + /// Determines whether the contains a specific disposable. + /// + /// Disposable to search for. + /// true if the disposable was found; otherwise, false. + /// is null. + public bool Contains(IDisposable item) + { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + + lock (_gate) + { + if (_disposed) + { + return false; + } + + var current = _disposables; + return current is List list + ? list.Contains(item) + : ((Dictionary)current).ContainsKey(item); + } + } + + /// + /// Copies the disposables contained in the to an array, starting at a particular array index. + /// + /// Array to copy the contained disposables to. + /// Target index at which to copy the first disposable of the group. + /// is null. + /// is less than zero. -or - is larger than or equal to the array length. + public void CopyTo(IDisposable[] array, int arrayIndex) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (arrayIndex < 0 || arrayIndex >= array.Length) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + lock (_gate) + { + // disposed composites are always empty + if (_disposed) + { + return; + } + + if (arrayIndex + _count > array.Length) + { + // there is not enough space beyond arrayIndex + // to accommodate all _count disposables in this composite + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + var i = arrayIndex; + + var current = _disposables; + + if (current is List currentList) + { + foreach (var d in currentList) + { + if (d != null) + { + array[i++] = d; + } + } + } + else + { + foreach (var kv in (Dictionary)current) + { + for (var j = 0; j < kv.Value; j++) + { + array[i++] = kv.Key; + } + } + } + } + } + + /// + /// Always returns false. + /// + public bool IsReadOnly => false; + + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator to iterate over the disposables. + public IEnumerator GetEnumerator() + { + lock (_gate) + { + if (_disposed || _count == 0) + { + return EmptyEnumerator; + } + + var current = _disposables; + + // the copy is unavoidable but the creation + // of an outer IEnumerable is avoidable + return new CompositeEnumerator(current is List currentList + ? currentList.ToArray() + : ((Dictionary)current).Keys.ToArray()); + } + } + + /// + /// Returns an enumerator that iterates through the . + /// + /// An enumerator to iterate over the disposables. + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Gets a value that indicates whether the object is disposed. + /// + public bool IsDisposed => Volatile.Read(ref _disposed); + + /// + /// An empty enumerator for the + /// method to avoid allocation on disposed or empty composites. + /// + private static readonly CompositeEnumerator EmptyEnumerator = + new([]); + + /// + /// An enumerator for an array of disposables. + /// + private sealed class CompositeEnumerator : IEnumerator + { + private readonly IDisposable?[] _disposables; + private int _index; + + public CompositeEnumerator(IDisposable?[] disposables) + { + _disposables = disposables; + _index = -1; + } + + public IDisposable Current => _disposables[_index]!; // NB: _index is only advanced to non-null positions. + + object IEnumerator.Current => _disposables[_index]!; + + public void Dispose() + { + // Avoid retention of the referenced disposables + // beyond the lifecycle of the enumerator. + // Not sure if this happens by default to + // generic array enumerators though. + var disposables = _disposables; + Array.Clear(disposables, 0, disposables.Length); + } + + public bool MoveNext() + { + var disposables = _disposables; + + for (;;) + { + var idx = ++_index; + + if (idx >= disposables.Length) + { + return false; + } + + // inlined that filter for null elements + if (disposables[idx] != null) + { + return true; + } + } + } + + public void Reset() + { + _index = -1; + } + } +} diff --git a/src/ReactiveUI/Temp/DisposableMixins.cs b/src/ReactiveUI/Temp/DisposableMixins.cs new file mode 100644 index 0000000000..12cd4be41c --- /dev/null +++ b/src/ReactiveUI/Temp/DisposableMixins.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. +// + +#nullable enable + +namespace System.Reactive.Disposables; + +/// +/// Provides extension methods associated with the . +/// +public static class DisposableMixins +{ + /// + /// Ensures the provided disposable is disposed with the specified . + /// + /// The type of the disposable. + /// The disposable to add to the composite. + /// The composite the disposable is added to. + /// The , to allow fluent use. + public static T DisposeWith(this T item, CompositeDisposable compositeDisposable) + where T : IDisposable + { + ArgumentExceptionHelper.ThrowIfNull(compositeDisposable); + compositeDisposable.Add(item); + return item; + } +} diff --git a/src/ReactiveUI/Temp/ICancelable.cs b/src/ReactiveUI/Temp/ICancelable.cs new file mode 100644 index 0000000000..b7e21d7c35 --- /dev/null +++ b/src/ReactiveUI/Temp/ICancelable.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. +// + +#nullable enable + +namespace System.Reactive.Disposables; + +/// +/// Disposable resource with disposal state tracking. +/// +public interface ICancelable : IDisposable +{ + /// + /// Gets a value that indicates whether the object is disposed. + /// + bool IsDisposed { get; } +} diff --git a/src/ReactiveUI/Temp/IScheduler.cs b/src/ReactiveUI/Temp/IScheduler.cs new file mode 100644 index 0000000000..71c96d860a --- /dev/null +++ b/src/ReactiveUI/Temp/IScheduler.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. +// + +namespace System.Reactive.Concurrency; + +/// +/// Represents an object that schedules units of work. +/// +public interface IScheduler +{ + /// + /// Gets the scheduler's notion of current time. + /// + DateTimeOffset Now { get; } + + /// + /// Schedules an action to be executed. + /// + /// The type of the state passed to the scheduled action. + /// State passed to the action to be executed. + /// Action to be executed. + /// The disposable object used to cancel the scheduled action (best effort). + IDisposable Schedule(TState state, Func action); + + /// + /// Schedules an action to be executed after dueTime. + /// + /// The type of the state passed to the scheduled action. + /// State passed to the action to be executed. + /// Relative time after which to execute the action. + /// Action to be executed. + /// The disposable object used to cancel the scheduled action (best effort). + IDisposable Schedule(TState state, TimeSpan dueTime, Func action); + + /// + /// Schedules an action to be executed at dueTime. + /// + /// The type of the state passed to the scheduled action. + /// State passed to the action to be executed. + /// Absolute time at which to execute the action. + /// Action to be executed. + /// The disposable object used to cancel the scheduled action (best effort). + IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action); +} diff --git a/src/ReactiveUI/Temp/ISubject.cs b/src/ReactiveUI/Temp/ISubject.cs new file mode 100644 index 0000000000..15624c1f8f --- /dev/null +++ b/src/ReactiveUI/Temp/ISubject.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. +// + +#nullable enable + +namespace System.Reactive.Subjects; + +/// +/// Represents an object that is both an observable sequence as well as an observer. +/// +/// The type of the elements processed by the subject. +/// The type of the elements produced by the subject. +public interface ISubject : IObserver, IObservable +{ +} + +/// +/// Represents an object that is both an observable sequence as well as an observer. +/// +/// The type of the elements processed by the subject. +public interface ISubject : ISubject +{ +} diff --git a/src/ReactiveUI/Temp/Schedulers.cs b/src/ReactiveUI/Temp/Schedulers.cs new file mode 100644 index 0000000000..c449859447 --- /dev/null +++ b/src/ReactiveUI/Temp/Schedulers.cs @@ -0,0 +1,134 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. +// + +#nullable enable + +using ReactiveUI.Internal; + +namespace System.Reactive.Concurrency; + +/// +/// Temporary migration shim. A scheduler whose scheduling methods are not yet implemented; provided so code that +/// references the System.Reactive scheduler types compiles while the owned scheduler layer is built out. +/// +public abstract class UnimplementedScheduler : IScheduler +{ + private const string NotImplementedMessage = + "Scheduling is not implemented yet during the System.Reactive removal. This is a temporary migration shim."; + + /// + public DateTimeOffset Now => DateTimeOffset.Now; + + /// + public IDisposable Schedule(TState state, Func action) => + throw new NotImplementedException(NotImplementedMessage); + + /// + public IDisposable Schedule(TState state, TimeSpan dueTime, Func action) => + throw new NotImplementedException(NotImplementedMessage); + + /// + public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) => + throw new NotImplementedException(NotImplementedMessage); +} + +/// Temporary migration shim for CurrentThreadScheduler. +public sealed class CurrentThreadScheduler : UnimplementedScheduler +{ + /// Gets the singleton instance. + public static CurrentThreadScheduler Instance { get; } = new(); +} + +/// Temporary migration shim for TaskPoolScheduler. +public sealed class TaskPoolScheduler : UnimplementedScheduler +{ + /// Gets the default instance. + public static TaskPoolScheduler Default { get; } = new(); +} + +/// Temporary migration shim for DefaultScheduler. +public sealed class DefaultScheduler : UnimplementedScheduler +{ + /// Gets the singleton instance. + public static DefaultScheduler Instance { get; } = new(); +} + +/// Temporary migration shim for ImmediateScheduler. +public sealed class ImmediateScheduler : UnimplementedScheduler +{ + /// Gets the singleton instance. + public static ImmediateScheduler Instance { get; } = new(); +} + +/// +/// Temporary migration shims for the System.Reactive convenience extension methods (which +/// are not declared on the interface). They forward to the stateful Schedule<TState> members. +/// +public static class SchedulerExtensions +{ + /// Schedules an action to be executed. + /// The scheduler to run the action on. + /// The action to execute. + /// A disposable used to cancel the scheduled action. + public static IDisposable Schedule(this IScheduler scheduler, Action action) + { + ArgumentExceptionHelper.ThrowIfNull(scheduler); + ArgumentExceptionHelper.ThrowIfNull(action); + return scheduler.Schedule(action, static (_, a) => + { + a(); + return EmptyDisposable.Instance; + }); + } + + /// Schedules an action to be executed after the specified relative time. + /// The scheduler to run the action on. + /// The relative time after which to execute the action. + /// The action to execute. + /// A disposable used to cancel the scheduled action. + public static IDisposable Schedule(this IScheduler scheduler, TimeSpan dueTime, Action action) + { + ArgumentExceptionHelper.ThrowIfNull(scheduler); + ArgumentExceptionHelper.ThrowIfNull(action); + return scheduler.Schedule(action, dueTime, static (_, a) => + { + a(); + return EmptyDisposable.Instance; + }); + } + + /// Schedules an action to be executed at the specified absolute time. + /// The scheduler to run the action on. + /// The absolute time at which to execute the action. + /// The action to execute. + /// A disposable used to cancel the scheduled action. + public static IDisposable Schedule(this IScheduler scheduler, DateTimeOffset dueTime, Action action) + { + ArgumentExceptionHelper.ThrowIfNull(scheduler); + ArgumentExceptionHelper.ThrowIfNull(action); + return scheduler.Schedule(action, dueTime, static (_, a) => + { + a(); + return EmptyDisposable.Instance; + }); + } +} + +/// Temporary migration shim for the static Scheduler entry points. +public static class Scheduler +{ + /// Gets the immediate scheduler. + public static IScheduler Immediate => ImmediateScheduler.Instance; + + /// Gets the current-thread scheduler. + public static IScheduler CurrentThread => CurrentThreadScheduler.Instance; + + /// Gets the default scheduler. + public static IScheduler Default => DefaultScheduler.Instance; + + /// Gets the task-pool scheduler. + public static IScheduler TaskPool => TaskPoolScheduler.Default; +} diff --git a/src/ReactiveUI/Temp/Unit.cs b/src/ReactiveUI/Temp/Unit.cs new file mode 100644 index 0000000000..29f44e4e71 --- /dev/null +++ b/src/ReactiveUI/Temp/Unit.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace System.Reactive; + +/// +/// Represents a type with a single value. This type is often used to denote the successful completion of a void-returning method (C#) or a Sub procedure (Visual Basic). +/// +public readonly struct Unit : IEquatable +{ + /// + /// Determines whether the specified value is equal to the current . Because has a single value, this always returns true. + /// + /// An object to compare to the current value. + /// Because has a single value, this always returns true. + public bool Equals(Unit other) => true; + + /// + /// Determines whether the specified System.Object is equal to the current . + /// + /// The System.Object to compare with the current . + /// true if the specified System.Object is a value; otherwise, false. + public override bool Equals(object obj) => obj is Unit; + + /// + /// Returns the hash code for the current value. + /// + /// A hash code for the current value. + public override int GetHashCode() => 0; + + /// + /// Returns a string representation of the current value. + /// + /// String representation of the current value. + public override string ToString() => "()"; + + /// + /// Determines whether the two specified values are equal. Because has a single value, this always returns true. + /// + /// The first value to compare. + /// The second value to compare. + /// Because has a single value, this always returns true. +#pragma warning disable IDE0060 // (Remove unused parameter.) Required part of public API + public static bool operator ==(Unit first, Unit second) => true; + + /// + /// Determines whether the two specified values are not equal. Because has a single value, this always returns false. + /// + /// The first value to compare. + /// The second value to compare. + /// Because has a single value, this always returns false. + public static bool operator !=(Unit first, Unit second) => false; +#pragma warning restore IDE0060 + + /// + /// Gets the single value. + /// + public static Unit Default => default; +} diff --git a/src/ReactiveUI/UnhandledErrorException.cs b/src/ReactiveUI/UnhandledErrorException.cs index 78ba41cffe..439a61bb3e 100644 --- a/src/ReactiveUI/UnhandledErrorException.cs +++ b/src/ReactiveUI/UnhandledErrorException.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/VariadicTemplates.cs b/src/ReactiveUI/VariadicTemplates.cs deleted file mode 100644 index 3f6b1e1696..0000000000 --- a/src/ReactiveUI/VariadicTemplates.cs +++ /dev/null @@ -1,5287 +0,0 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -#nullable enable - -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -//------------------------------------------------------------------------------ - -using System; -using System.Reactive.Linq; -using System.Linq; -using System.Linq.Expressions; -using System.Diagnostics.CodeAnalysis; - - -namespace ReactiveUI -{ - /// Extension methods associated with the WhenAny/WhenAnyValue classes. - [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static class WhenAnyMixin - { - /// - /// WhenAnyValue allows you to observe whenever the value of a - /// property on an object has changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The first property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1) - { - return sender!.WhenAny(property1, (IObservedChange c1) => c1.Value); - } - - /// - /// AOT-friendly overload that avoids expression trees by using a property name. - /// - /// The object where the property chain starts. - /// The property name to observe. - public static IObservable WhenAnyValue( - this TSender? sender, - string propertyName) - { - return sender!.ObservableForProperty(propertyName, beforeChange: false, skipInitial: false, isDistinct: true) - .Select(x => x.Value); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of a - /// property on an object has changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The first property chain to reference. This will be a expression pointing to a end property or field. - /// if set to true [is distinct]. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - bool isDistinct) - { - return sender!.WhenAny(property1, (IObservedChange c1) => c1.Value, isDistinct); - } - - /// - /// AOT-friendly overload that avoids expression trees by using a property name and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string propertyName, - bool isDistinct) - { - return sender!.ObservableForProperty(propertyName, beforeChange: false, skipInitial: false, isDistinct: isDistinct) - .Select(x => x.Value); - } - - - - - - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Func selector) - { - return sender!.WhenAny(property1, - (c1) => - selector(c1.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return o1.Select(selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, - (c1) => - selector(c1.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return o1.Select(selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Func, TRet> selector) - { - return sender!.ObservableForProperty(property1, false, false).Select(selector); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - Func, TRet> selector) - { - return sender!.ObservableForProperty(property1Name, false, false) - .Select(c1 => selector(c1)); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Func, TRet> selector, - bool isDistinct) - { - return sender!.ObservableForProperty(property1, false, false, isDistinct).Select(selector); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - Func, TRet> selector, - bool isDistinct) - { - return sender!.ObservableForProperty(property1Name, false, false, isDistinct) - .Select(c1 => selector(c1)); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Func, TRet> selector) - { - return ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false).Select(selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Func, TRet> selector, - bool isDistinct) - { - return ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct).Select(selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2 - ) - { - return sender!.WhenAny(property1, property2, - (c1, c2) => - (c1.Value, c2.Value)); - } - - /// - /// AOT-friendly tuple overloads using property names instead of expressions. - /// - public static IObservable<(T1,T2)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name ) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value) - , (v1,v2) => - (v1,v2) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2 - , - bool isDistinct) - { - return sender!.WhenAny(property1, property2, - (c1, c2) => - (c1.Value, c2.Value), - isDistinct); - } - - /// - /// AOT-friendly tuple overloads using property names with distinct option. - /// - public static IObservable<(T1,T2)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name , - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value) - , (v1,v2) => - (v1,v2) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Func selector) - { - return sender!.WhenAny(property1, property2, - (c1, c2) => - selector(c1.Value, c2.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, - (c1, c2) => - selector(c1.Value, c2.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Func, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - Func, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Func, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - Func, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Func, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Func, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3 - ) - { - return sender!.WhenAny(property1, property2, property3, - (c1, c2, c3) => - (c1.Value, c2.Value, c3.Value)); - } - - /// - /// AOT-friendly tuple overloads using property names instead of expressions. - /// - public static IObservable<(T1,T2,T3)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name ) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value) - , (v1,v2,v3) => - (v1,v2,v3) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3 - , - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, - (c1, c2, c3) => - (c1.Value, c2.Value, c3.Value), - isDistinct); - } - - /// - /// AOT-friendly tuple overloads using property names with distinct option. - /// - public static IObservable<(T1,T2,T3)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name , - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value) - , (v1,v2,v3) => - (v1,v2,v3) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, - (c1, c2, c3) => - selector(c1.Value, c2.Value, c3.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, - (c1, c2, c3) => - selector(c1.Value, c2.Value, c3.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Func, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - Func, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Func, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - Func, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Func, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Func, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4 - ) - { - return sender!.WhenAny(property1, property2, property3, property4, - (c1, c2, c3, c4) => - (c1.Value, c2.Value, c3.Value, c4.Value)); - } - - /// - /// AOT-friendly tuple overloads using property names instead of expressions. - /// - public static IObservable<(T1,T2,T3,T4)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name ) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value) - , (v1,v2,v3,v4) => - (v1,v2,v3,v4) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4 - , - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, - (c1, c2, c3, c4) => - (c1.Value, c2.Value, c3.Value, c4.Value), - isDistinct); - } - - /// - /// AOT-friendly tuple overloads using property names with distinct option. - /// - public static IObservable<(T1,T2,T3,T4)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name , - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value) - , (v1,v2,v3,v4) => - (v1,v2,v3,v4) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, - (c1, c2, c3, c4) => - selector(c1.Value, c2.Value, c3.Value, c4.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, - (c1, c2, c3, c4) => - selector(c1.Value, c2.Value, c3.Value, c4.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4,T5)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5 - ) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, - (c1, c2, c3, c4, c5) => - (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value)); - } - - /// - /// AOT-friendly tuple overloads using property names instead of expressions. - /// - public static IObservable<(T1,T2,T3,T4,T5)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name, string property5Name ) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value), - o5.Select(x => x.Value) - , (v1,v2,v3,v4,v5) => - (v1,v2,v3,v4,v5) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4,T5)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5 - , - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, - (c1, c2, c3, c4, c5) => - (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value), - isDistinct); - } - - /// - /// AOT-friendly tuple overloads using property names with distinct option. - /// - public static IObservable<(T1,T2,T3,T4,T5)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name, string property5Name , - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value), - o5.Select(x => x.Value) - , (v1,v2,v3,v4,v5) => - (v1,v2,v3,v4,v5) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, - (c1, c2, c3, c4, c5) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, - (c1, c2, c3, c4, c5) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4,T5,T6)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6 - ) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, - (c1, c2, c3, c4, c5, c6) => - (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value)); - } - - /// - /// AOT-friendly tuple overloads using property names instead of expressions. - /// - public static IObservable<(T1,T2,T3,T4,T5,T6)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name, string property5Name, string property6Name ) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value), - o5.Select(x => x.Value), - o6.Select(x => x.Value) - , (v1,v2,v3,v4,v5,v6) => - (v1,v2,v3,v4,v5,v6) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4,T5,T6)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6 - , - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, - (c1, c2, c3, c4, c5, c6) => - (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value), - isDistinct); - } - - /// - /// AOT-friendly tuple overloads using property names with distinct option. - /// - public static IObservable<(T1,T2,T3,T4,T5,T6)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name, string property5Name, string property6Name , - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value), - o5.Select(x => x.Value), - o6.Select(x => x.Value) - , (v1,v2,v3,v4,v5,v6) => - (v1,v2,v3,v4,v5,v6) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, - (c1, c2, c3, c4, c5, c6) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, - (c1, c2, c3, c4, c5, c6) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - sender!.ObservableForProperty(property6, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - sender!.ObservableForProperty(property6Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - sender!.ObservableForProperty(property6, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - sender!.ObservableForProperty(property6Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4,T5,T6,T7)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7 - ) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, - (c1, c2, c3, c4, c5, c6, c7) => - (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value)); - } - - /// - /// AOT-friendly tuple overloads using property names instead of expressions. - /// - public static IObservable<(T1,T2,T3,T4,T5,T6,T7)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name, string property5Name, string property6Name, string property7Name ) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value), - o5.Select(x => x.Value), - o6.Select(x => x.Value), - o7.Select(x => x.Value) - , (v1,v2,v3,v4,v5,v6,v7) => - (v1,v2,v3,v4,v5,v6,v7) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable<(T1,T2,T3,T4,T5,T6,T7)> WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7 - , - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, - (c1, c2, c3, c4, c5, c6, c7) => - (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value), - isDistinct); - } - - /// - /// AOT-friendly tuple overloads using property names with distinct option. - /// - public static IObservable<(T1,T2,T3,T4,T5,T6,T7)> WhenAnyValue( - this TSender? sender, - string property1Name, string property2Name, string property3Name, string property4Name, string property5Name, string property6Name, string property7Name , - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - return Observable.CombineLatest( - o1.Select(x => x.Value), - o2.Select(x => x.Value), - o3.Select(x => x.Value), - o4.Select(x => x.Value), - o5.Select(x => x.Value), - o6.Select(x => x.Value), - o7.Select(x => x.Value) - , (v1,v2,v3,v4,v5,v6,v7) => - (v1,v2,v3,v4,v5,v6,v7) - ); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, - (c1, c2, c3, c4, c5, c6, c7) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, - (c1, c2, c3, c4, c5, c6, c7) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - sender!.ObservableForProperty(property6, false, false), - sender!.ObservableForProperty(property7, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - sender!.ObservableForProperty(property6Name, false, false), - sender!.ObservableForProperty(property7Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - sender!.ObservableForProperty(property6, false, false, isDistinct), - sender!.ObservableForProperty(property7, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - sender!.ObservableForProperty(property6Name, false, false, isDistinct), - sender!.ObservableForProperty(property7Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false, isDistinct), - selector - ); - } - - - - - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, - (c1, c2, c3, c4, c5, c6, c7, c8) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, - (c1, c2, c3, c4, c5, c6, c7, c8) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - sender!.ObservableForProperty(property6, false, false), - sender!.ObservableForProperty(property7, false, false), - sender!.ObservableForProperty(property8, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - sender!.ObservableForProperty(property6Name, false, false), - sender!.ObservableForProperty(property7Name, false, false), - sender!.ObservableForProperty(property8Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - sender!.ObservableForProperty(property6, false, false, isDistinct), - sender!.ObservableForProperty(property7, false, false, isDistinct), - sender!.ObservableForProperty(property8, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - sender!.ObservableForProperty(property6Name, false, false, isDistinct), - sender!.ObservableForProperty(property7Name, false, false, isDistinct), - sender!.ObservableForProperty(property8Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false, isDistinct), - selector - ); - } - - - - - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, - (c1, c2, c3, c4, c5, c6, c7, c8, c9) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, - (c1, c2, c3, c4, c5, c6, c7, c8, c9) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - sender!.ObservableForProperty(property6, false, false), - sender!.ObservableForProperty(property7, false, false), - sender!.ObservableForProperty(property8, false, false), - sender!.ObservableForProperty(property9, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - sender!.ObservableForProperty(property6Name, false, false), - sender!.ObservableForProperty(property7Name, false, false), - sender!.ObservableForProperty(property8Name, false, false), - sender!.ObservableForProperty(property9Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - sender!.ObservableForProperty(property6, false, false, isDistinct), - sender!.ObservableForProperty(property7, false, false, isDistinct), - sender!.ObservableForProperty(property8, false, false, isDistinct), - sender!.ObservableForProperty(property9, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - sender!.ObservableForProperty(property6Name, false, false, isDistinct), - sender!.ObservableForProperty(property7Name, false, false, isDistinct), - sender!.ObservableForProperty(property8Name, false, false, isDistinct), - sender!.ObservableForProperty(property9Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false, isDistinct), - selector - ); - } - - - - - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, property10, - (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9, - o10 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, property10, - (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9, - o10 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - sender!.ObservableForProperty(property6, false, false), - sender!.ObservableForProperty(property7, false, false), - sender!.ObservableForProperty(property8, false, false), - sender!.ObservableForProperty(property9, false, false), - sender!.ObservableForProperty(property10, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - sender!.ObservableForProperty(property6Name, false, false), - sender!.ObservableForProperty(property7Name, false, false), - sender!.ObservableForProperty(property8Name, false, false), - sender!.ObservableForProperty(property9Name, false, false), - sender!.ObservableForProperty(property10Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - sender!.ObservableForProperty(property6, false, false, isDistinct), - sender!.ObservableForProperty(property7, false, false, isDistinct), - sender!.ObservableForProperty(property8, false, false, isDistinct), - sender!.ObservableForProperty(property9, false, false, isDistinct), - sender!.ObservableForProperty(property10, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - sender!.ObservableForProperty(property6Name, false, false, isDistinct), - sender!.ObservableForProperty(property7Name, false, false, isDistinct), - sender!.ObservableForProperty(property8Name, false, false, isDistinct), - sender!.ObservableForProperty(property9Name, false, false, isDistinct), - sender!.ObservableForProperty(property10Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Expression? property10, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property10, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Expression? property10, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property10, false, false, isDistinct), - selector - ); - } - - - - - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, property10, property11, - (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value, c11.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9, - o10, - o11 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, property10, property11, - (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value, c11.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9, - o10, - o11 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - sender!.ObservableForProperty(property6, false, false), - sender!.ObservableForProperty(property7, false, false), - sender!.ObservableForProperty(property8, false, false), - sender!.ObservableForProperty(property9, false, false), - sender!.ObservableForProperty(property10, false, false), - sender!.ObservableForProperty(property11, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - sender!.ObservableForProperty(property6Name, false, false), - sender!.ObservableForProperty(property7Name, false, false), - sender!.ObservableForProperty(property8Name, false, false), - sender!.ObservableForProperty(property9Name, false, false), - sender!.ObservableForProperty(property10Name, false, false), - sender!.ObservableForProperty(property11Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - sender!.ObservableForProperty(property6, false, false, isDistinct), - sender!.ObservableForProperty(property7, false, false, isDistinct), - sender!.ObservableForProperty(property8, false, false, isDistinct), - sender!.ObservableForProperty(property9, false, false, isDistinct), - sender!.ObservableForProperty(property10, false, false, isDistinct), - sender!.ObservableForProperty(property11, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - sender!.ObservableForProperty(property6Name, false, false, isDistinct), - sender!.ObservableForProperty(property7Name, false, false, isDistinct), - sender!.ObservableForProperty(property8Name, false, false, isDistinct), - sender!.ObservableForProperty(property9Name, false, false, isDistinct), - sender!.ObservableForProperty(property10Name, false, false, isDistinct), - sender!.ObservableForProperty(property11Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Expression? property10, - Expression? property11, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property10, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property11, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Expression? property10, - Expression? property11, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property10, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property11, false, false, isDistinct), - selector - ); - } - - - - - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The 12 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Expression> property12, - Func selector) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, property10, property11, property12, - (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value, c11.Value, c12.Value)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - string property12Name, - Func selector) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - var o12 = sender!.ObservableForProperty(property12Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9, - o10, - o11, - o12 - , selector); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The 12 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Expression> property12, - Func selector, - bool isDistinct) - { - return sender!.WhenAny(property1, property2, property3, property4, property5, property6, property7, property8, property9, property10, property11, property12, - (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) => - selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value, c11.Value, c12.Value), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - string property12Name, - Func selector, - bool isDistinct) - { - var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - var o12 = sender!.ObservableForProperty(property12Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - return Observable.CombineLatest( - o1, - o2, - o3, - o4, - o5, - o6, - o7, - o8, - o9, - o10, - o11, - o12 - , selector); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The 12 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Expression> property12, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false), - sender!.ObservableForProperty(property2, false, false), - sender!.ObservableForProperty(property3, false, false), - sender!.ObservableForProperty(property4, false, false), - sender!.ObservableForProperty(property5, false, false), - sender!.ObservableForProperty(property6, false, false), - sender!.ObservableForProperty(property7, false, false), - sender!.ObservableForProperty(property8, false, false), - sender!.ObservableForProperty(property9, false, false), - sender!.ObservableForProperty(property10, false, false), - sender!.ObservableForProperty(property11, false, false), - sender!.ObservableForProperty(property12, false, false), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - string property12Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false), - sender!.ObservableForProperty(property2Name, false, false), - sender!.ObservableForProperty(property3Name, false, false), - sender!.ObservableForProperty(property4Name, false, false), - sender!.ObservableForProperty(property5Name, false, false), - sender!.ObservableForProperty(property6Name, false, false), - sender!.ObservableForProperty(property7Name, false, false), - sender!.ObservableForProperty(property8Name, false, false), - sender!.ObservableForProperty(property9Name, false, false), - sender!.ObservableForProperty(property10Name, false, false), - sender!.ObservableForProperty(property11Name, false, false), - sender!.ObservableForProperty(property12Name, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The 12 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAny( - this TSender? sender, - Expression> property1, - Expression> property2, - Expression> property3, - Expression> property4, - Expression> property5, - Expression> property6, - Expression> property7, - Expression> property8, - Expression> property9, - Expression> property10, - Expression> property11, - Expression> property12, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1, false, false, isDistinct), - sender!.ObservableForProperty(property2, false, false, isDistinct), - sender!.ObservableForProperty(property3, false, false, isDistinct), - sender!.ObservableForProperty(property4, false, false, isDistinct), - sender!.ObservableForProperty(property5, false, false, isDistinct), - sender!.ObservableForProperty(property6, false, false, isDistinct), - sender!.ObservableForProperty(property7, false, false, isDistinct), - sender!.ObservableForProperty(property8, false, false, isDistinct), - sender!.ObservableForProperty(property9, false, false, isDistinct), - sender!.ObservableForProperty(property10, false, false, isDistinct), - sender!.ObservableForProperty(property11, false, false, isDistinct), - sender!.ObservableForProperty(property12, false, false, isDistinct), - selector - ); - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny( - this TSender? sender, - string property1Name, - string property2Name, - string property3Name, - string property4Name, - string property5Name, - string property6Name, - string property7Name, - string property8Name, - string property9Name, - string property10Name, - string property11Name, - string property12Name, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - sender!.ObservableForProperty(property1Name, false, false, isDistinct), - sender!.ObservableForProperty(property2Name, false, false, isDistinct), - sender!.ObservableForProperty(property3Name, false, false, isDistinct), - sender!.ObservableForProperty(property4Name, false, false, isDistinct), - sender!.ObservableForProperty(property5Name, false, false, isDistinct), - sender!.ObservableForProperty(property6Name, false, false, isDistinct), - sender!.ObservableForProperty(property7Name, false, false, isDistinct), - sender!.ObservableForProperty(property8Name, false, false, isDistinct), - sender!.ObservableForProperty(property9Name, false, false, isDistinct), - sender!.ObservableForProperty(property10Name, false, false, isDistinct), - sender!.ObservableForProperty(property11Name, false, false, isDistinct), - sender!.ObservableForProperty(property12Name, false, false, isDistinct), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The 12 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Expression? property10, - Expression? property11, - Expression? property12, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property10, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property11, false, false), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property12, false, false), - selector - ); - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// The 1 property chain to reference. This will be a expression pointing to a end property or field. - /// The 2 property chain to reference. This will be a expression pointing to a end property or field. - /// The 3 property chain to reference. This will be a expression pointing to a end property or field. - /// The 4 property chain to reference. This will be a expression pointing to a end property or field. - /// The 5 property chain to reference. This will be a expression pointing to a end property or field. - /// The 6 property chain to reference. This will be a expression pointing to a end property or field. - /// The 7 property chain to reference. This will be a expression pointing to a end property or field. - /// The 8 property chain to reference. This will be a expression pointing to a end property or field. - /// The 9 property chain to reference. This will be a expression pointing to a end property or field. - /// The 10 property chain to reference. This will be a expression pointing to a end property or field. - /// The 11 property chain to reference. This will be a expression pointing to a end property or field. - /// The 12 property chain to reference. This will be a expression pointing to a end property or field. - /// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - Expression? property1, - Expression? property2, - Expression? property3, - Expression? property4, - Expression? property5, - Expression? property6, - Expression? property7, - Expression? property8, - Expression? property9, - Expression? property10, - Expression? property11, - Expression? property12, - Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, - bool isDistinct) - { - return Observable.CombineLatest( - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property1, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property2, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property3, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property4, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property5, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property6, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property7, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property8, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property9, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property10, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property11, false, false, isDistinct), - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property12, false, false, isDistinct), - selector - ); - } - } - - /// A mixin which provides support for subscribing to observable properties. - [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static class WhenAnyObservableMixin - { - /// Observe a observable which is set to a property, and automatically subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The first observable to observe. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1) - where TSender : class - { - return sender.WhenAny(obs1, x => x.Value!.EmptyIfNull()).Switch(); - } - - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2) - where TSender : class - { - return sender.WhenAny(obs1, obs2, (o1, o2) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, (o1, o2, o3) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, (o1, o2, o3, o4) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, (o1, o2, o3, o4, o5) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 6 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5, Expression?>> obs6) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, (o1, o2, o3, o4, o5, o6) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 6 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 7 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5, Expression?>> obs6, Expression?>> obs7) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, (o1, o2, o3, o4, o5, o6, o7) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 6 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 7 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 8 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5, Expression?>> obs6, Expression?>> obs7, Expression?>> obs8) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, (o1, o2, o3, o4, o5, o6, o7, o8) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 6 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 7 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 8 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 9 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5, Expression?>> obs6, Expression?>> obs7, Expression?>> obs8, Expression?>> obs9) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, (o1, o2, o3, o4, o5, o6, o7, o8, o9) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 6 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 7 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 8 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 9 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 10 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5, Expression?>> obs6, Expression?>> obs7, Expression?>> obs8, Expression?>> obs9, Expression?>> obs10) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, obs10, (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull(), o10.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 6 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 7 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 8 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 9 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 10 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 11 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5, Expression?>> obs6, Expression?>> obs7, Expression?>> obs8, Expression?>> obs9, Expression?>> obs10, Expression?>> obs11) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, obs10, obs11, (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull(), o10.Value!.EmptyIfNull(), o11.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 2 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 3 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 4 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 5 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 6 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 7 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 8 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 9 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 10 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 11 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - /// The 12 property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1, Expression?>> obs2, Expression?>> obs3, Expression?>> obs4, Expression?>> obs5, Expression?>> obs6, Expression?>> obs7, Expression?>> obs8, Expression?>> obs9, Expression?>> obs10, Expression?>> obs11, Expression?>> obs12) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, obs10, obs11, obs12, (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12) => new[] {o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull(), o10.Value!.EmptyIfNull(), o11.Value!.EmptyIfNull(), o12.Value!.EmptyIfNull()}) - .Select(x => x.Merge()).Switch(); - } - - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, (o1, o2) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, (o1, o2, o3) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, (o1, o2, o3, o4) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, (o1, o2, o3, o4, o5) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The 6 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Expression?>> obs6, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, (o1, o2, o3, o4, o5, o6) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The 6 property chain to reference. - /// The 7 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Expression?>> obs6, - Expression?>> obs7, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, (o1, o2, o3, o4, o5, o6, o7) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The 6 property chain to reference. - /// The 7 property chain to reference. - /// The 8 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Expression?>> obs6, - Expression?>> obs7, - Expression?>> obs8, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, (o1, o2, o3, o4, o5, o6, o7, o8) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The 6 property chain to reference. - /// The 7 property chain to reference. - /// The 8 property chain to reference. - /// The 9 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Expression?>> obs6, - Expression?>> obs7, - Expression?>> obs8, - Expression?>> obs9, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, (o1, o2, o3, o4, o5, o6, o7, o8, o9) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The 6 property chain to reference. - /// The 7 property chain to reference. - /// The 8 property chain to reference. - /// The 9 property chain to reference. - /// The 10 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Expression?>> obs6, - Expression?>> obs7, - Expression?>> obs8, - Expression?>> obs9, - Expression?>> obs10, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, obs10, (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull(), o10.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The 6 property chain to reference. - /// The 7 property chain to reference. - /// The 8 property chain to reference. - /// The 9 property chain to reference. - /// The 10 property chain to reference. - /// The 11 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Expression?>> obs6, - Expression?>> obs7, - Expression?>> obs8, - Expression?>> obs9, - Expression?>> obs10, - Expression?>> obs11, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, obs10, obs11, (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull(), o10.Value!.EmptyIfNull(), o11.Value!.EmptyIfNull(), selector)) - .Switch(); - } - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The 1 property chain to reference. - /// The 2 property chain to reference. - /// The 3 property chain to reference. - /// The 4 property chain to reference. - /// The 5 property chain to reference. - /// The 6 property chain to reference. - /// The 7 property chain to reference. - /// The 8 property chain to reference. - /// The 9 property chain to reference. - /// The 10 property chain to reference. - /// The 11 property chain to reference. - /// The 12 property chain to reference. - /// The selector which will determine the final value from the properties. This must be an observable. - public static IObservable WhenAnyObservable(this TSender? sender, - Expression?>> obs1, - Expression?>> obs2, - Expression?>> obs3, - Expression?>> obs4, - Expression?>> obs5, - Expression?>> obs6, - Expression?>> obs7, - Expression?>> obs8, - Expression?>> obs9, - Expression?>> obs10, - Expression?>> obs11, - Expression?>> obs12, - Func selector) - where TSender : class - { - return sender.WhenAny(obs1, obs2, obs3, obs4, obs5, obs6, obs7, obs8, obs9, obs10, obs11, obs12, (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12) => Observable.CombineLatest(o1.Value!.EmptyIfNull(), o2.Value!.EmptyIfNull(), o3.Value!.EmptyIfNull(), o4.Value!.EmptyIfNull(), o5.Value!.EmptyIfNull(), o6.Value!.EmptyIfNull(), o7.Value!.EmptyIfNull(), o8.Value!.EmptyIfNull(), o9.Value!.EmptyIfNull(), o10.Value!.EmptyIfNull(), o11.Value!.EmptyIfNull(), o12.Value!.EmptyIfNull(), selector)) - .Switch(); - } -} - - internal static class ObservableExtensions - { - public static IObservable EmptyIfNull(this IObservable @this) - { - return @this ?? Observable.Empty(); - } - } -} diff --git a/src/ReactiveUI/VariadicTemplates.tt b/src/ReactiveUI/VariadicTemplates.tt deleted file mode 100644 index dcb37e2b88..0000000000 --- a/src/ReactiveUI/VariadicTemplates.tt +++ /dev/null @@ -1,542 +0,0 @@ -<#@ template debug="false" hostspecific="false" language="C#" #> -<#@ assembly name="System.Core.dll" #> -<#@ import namespace="System.Linq" #> -<#@ output extension=".cs" #> -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for full license information. - -#nullable enable - -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -//------------------------------------------------------------------------------ - -using System; -using System.Reactive.Linq; -using System.Linq; -using System.Linq.Expressions; -using System.Diagnostics.CodeAnalysis; - -<# -// NB: maxFuncLength is 4 on WP7, 12 on every other platform. -// VariadicTemplates_WP7.tt should always be a copy of VariadicTemplates.tt -// except for this section - -int maxFuncLength = 12; -#> - -namespace ReactiveUI -{ - /// Extension methods associated with the WhenAny/WhenAnyValue classes. - [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static class WhenAnyMixin - { - /// - /// WhenAnyValue allows you to observe whenever the value of a - /// property on an object has changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The first property chain to reference. This will be a expression pointing to a end property or field. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1) - { - return sender!.WhenAny(property1, (IObservedChange c1) => c1.Value); - } - - /// - /// AOT-friendly overload that avoids expression trees by using a property name. - /// - /// The object where the property chain starts. - /// The property name to observe. - public static IObservable WhenAnyValue( - this TSender? sender, - string propertyName) - { - return sender!.ObservableForProperty(propertyName, beforeChange: false, skipInitial: false, isDistinct: true) - .Select(x => x.Value); - } - - /// - /// WhenAnyValue allows you to observe whenever the value of a - /// property on an object has changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// The first property chain to reference. This will be a expression pointing to a end property or field. - /// if set to true [is distinct]. - public static IObservable WhenAnyValue( - this TSender? sender, - Expression> property1, - bool isDistinct) - { - return sender!.WhenAny(property1, (IObservedChange c1) => c1.Value, isDistinct); - } - - /// - /// AOT-friendly overload that avoids expression trees by using a property name and distinct option. - /// - public static IObservable WhenAnyValue( - this TSender? sender, - string propertyName, - bool isDistinct) - { - return sender!.ObservableForProperty(propertyName, beforeChange: false, skipInitial: false, isDistinct: isDistinct) - .Select(x => x.Value); - } - - <# for(int length=1; length <= maxFuncLength; length++) { #> - <# var templParams = Enumerable.Range(1, length).Select(x => "T" + x.ToString()); #> - <# var templParamsDec = Enumerable.Range(1, length).Select(x => "T" + x.ToString()); #> - <# string selectorTypeParams = String.Join(", ", templParams.Select(x => String.Format("IObservedChange", x))); #> - <# string valuePropertyParams = String.Join(", ", Enumerable.Range(1, length).Select(x => String.Format("property{0}", x))); #> - <# string valueSelectorParams = String.Join(", ", Enumerable.Range(1, length).Select(x => "c" + x)); #> - <# string valueSelectorArgs = String.Join(", ", Enumerable.Range(1, length).Select(x => String.Format("c{0}.Value", x))); #> - <# string dynamicSelectorTypeParams = String.Join(", ", templParams.Select(x => "IObservedChange")); #> - <# string selectorCall = "selector(" + String.Join(", ", Enumerable.Range(1, length).Select(x => "islot" + x.ToString())) + ")"; #> - - <# if (length != 1 && length <= 7) { #>/// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #> - public static IObservable<(<#= String.Join(",", templParams) #>)> WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression>> property<#=i#><# if (i != length) { #>,<# } #> - - <# } #>) - { - return sender!.WhenAny(<#= valuePropertyParams #>, - (<#= valueSelectorParams #>) => - (<#= valueSelectorArgs #>)); - } - <# } #> - - <# if (length != 1 && length <= 7) { #>/// - /// AOT-friendly tuple overloads using property names instead of expressions. - /// - public static IObservable<(<#= String.Join(",", templParams) #>)> WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - string property<#=i#>Name<# if (i != length) { #>,<# } #> - <# } #>) - { - <# for(int i=1; i <= length; i++) { #> - var o<#=i#> = sender!.ObservableForProperty>(property<#=i#>Name, beforeChange: false, skipInitial: false, isDistinct: true); - <# } #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - o<#=i#>.Select(x => x.Value)<#= i != length ? "," : string.Empty #> - <# } #> - , (<#= String.Join(",", Enumerable.Range(1, length).Select(x => "v" + x)) #>) => - (<#= String.Join(",", Enumerable.Range(1, length).Select(x => "v" + x)) #>) - ); - } - <# } #> - - <# if (length != 1 && length <= 7) { #>/// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #> - public static IObservable<(<#= String.Join(",", templParams) #>)> WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression>> property<#=i#><# if (i != length) { #>,<# } #> - - <# } #>, - bool isDistinct) - { - return sender!.WhenAny(<#= valuePropertyParams #>, - (<#= valueSelectorParams #>) => - (<#= valueSelectorArgs #>), - isDistinct); - } - <# } #> - - <# if (length != 1 && length <= 7) { #>/// - /// AOT-friendly tuple overloads using property names with distinct option. - /// - public static IObservable<(<#= String.Join(",", templParams) #>)> WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - string property<#=i#>Name<# if (i != length) { #>,<# } #> - <# } #>, - bool isDistinct) - { - <# for(int i=1; i <= length; i++) { #> - var o<#=i#> = sender!.ObservableForProperty>(property<#=i#>Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); - <# } #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - o<#=i#>.Select(x => x.Value)<#= i != length ? "," : string.Empty #> - <# } #> - , (<#= String.Join(",", Enumerable.Range(1, length).Select(x => "v" + x)) #>) => - (<#= String.Join(",", Enumerable.Range(1, length).Select(x => "v" + x)) #>) - ); - } - <# } #> - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #>/// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression>> property<#=i#>, - <# } #> - Func<<#= String.Join(",", templParams) #>, TRet> selector) - { - return sender!.WhenAny(<#= valuePropertyParams #>, - (<#= valueSelectorParams #>) => - selector(<#= valueSelectorArgs #>)); - } - - /// - /// AOT-friendly selector overload using property names. - /// - public static IObservable WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - string property<#=i#>Name, - <# } #> - Func<<#= String.Join(",", templParams) #>, TRet> selector) - { - <# for(int i=1; i <= length; i++) { #> - var o<#=i#> = sender!.ObservableForProperty>(property<#=i#>Name, beforeChange: false, skipInitial: false, isDistinct: true).Select(x => x.Value); - <# } #> - <# if (length == 1) { #> - return o1.Select(selector); - <# } else { #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - o<#=i#><#= i != length ? "," : string.Empty #> - <# } #> - , selector); - <# } #> - } - - /// - /// WhenAnyValue allows you to observe whenever the value of one or more - /// properties on an object have changed, providing an initial value when - /// the Observable is set up, unlike ObservableForProperty(). Use this - /// method in constructors to set up bindings between properties that also - /// need an initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #>/// The selector which will determine the final value from the properties. - public static IObservable WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression>> property<#=i#>, - <# } #> - Func<<#= String.Join(",", templParams) #>, TRet> selector, - bool isDistinct) - { - return sender!.WhenAny(<#= valuePropertyParams #>, - (<#= valueSelectorParams #>) => - selector(<#= valueSelectorArgs #>), - isDistinct); - } - - /// - /// AOT-friendly selector overload using property names and distinct option. - /// - public static IObservable WhenAnyValue>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - string property<#=i#>Name, - <# } #> - Func<<#= String.Join(",", templParams) #>, TRet> selector, - bool isDistinct) - { - <# for(int i=1; i <= length; i++) { #> - var o<#=i#> = sender!.ObservableForProperty>(property<#=i#>Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct).Select(x => x.Value); - <# } #> - <# if (length == 1) { #> - return o1.Select(selector); - <# } else { #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - o<#=i#><#= i != length ? "," : string.Empty #> - <# } #> - , selector); - <# } #> - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - <# for(int i=1; i <= length; i++) { #> - /// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #>/// The selector which will determine the final value from the properties. - public static IObservable WhenAny>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression>> property<#=i#>, - <# } #> - Func<<#= selectorTypeParams #>, TRet> selector) - { - <# if (length == 1){ #> - return sender!.ObservableForProperty(property<#=1#>, false, false).Select(selector); - <# }else{ #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - sender!.ObservableForProperty(property<#=i#>, false, false), - <# } #> - selector - ); - <# } #> - } - - /// - /// AOT-friendly WhenAny overload using property names. - /// - public static IObservable WhenAny>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - string property<#=i#>Name, - <# } #> - Func<<#= selectorTypeParams #>, TRet> selector) - { - <# if (length == 1){ #> - return sender!.ObservableForProperty(property1Name, false, false) - .Select(c1 => selector(c1)); - <# }else{ #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - sender!.ObservableForProperty>(property<#=i#>Name, false, false), - <# } #> - selector - ); - <# } #> - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - /// if set to true [is distinct]. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #>/// The selector which will determine the final value from the properties. - public static IObservable WhenAny>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression>> property<#=i#>, - <# } #> - Func<<#= selectorTypeParams #>, TRet> selector, - bool isDistinct) - { - <# if (length == 1){ #> - return sender!.ObservableForProperty(property<#=1#>, false, false, isDistinct).Select(selector); - <# }else{ #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - sender!.ObservableForProperty(property<#=i#>, false, false, isDistinct), - <# } #> - selector - ); - <# } #> - } - - /// - /// AOT-friendly WhenAny overload using property names and distinct option. - /// - public static IObservable WhenAny>( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - string property<#=i#>Name, - <# } #> - Func<<#= selectorTypeParams #>, TRet> selector, - bool isDistinct) - { - <# if (length == 1){ #> - return sender!.ObservableForProperty(property1Name, false, false, isDistinct) - .Select(c1 => selector(c1)); - <# }else{ #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - sender!.ObservableForProperty>(property<#=i#>Name, false, false, isDistinct), - <# } #> - selector - ); - <# } #> - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #>/// The selector which will determine the final value from the properties. - public static IObservable WhenAnyDynamic( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression? property<#=i#>, - <# } #> - Func<<#= dynamicSelectorTypeParams #>, TRet> selector) - { - <# if (length == 1){ #> - return ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property<#=1#>, false, false).Select(selector); - <# }else{ #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property<#=i#>, false, false), - <# } #> - selector - ); - <# } #> - } - - /// - /// WhenAny allows you to observe whenever one or more properties on an - /// object have changed, providing an initial value when the Observable - /// is set up, unlike ObservableForProperty(). Use this method in - /// constructors to set up bindings between properties that also need an - /// initial setup. - /// - /// The object where the property chain starts. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. This will be a expression pointing to a end property or field. - <# } #>/// The selector which will determine the final value from the properties. - /// if set to true [is distinct]. - public static IObservable WhenAnyDynamic( - this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression? property<#=i#>, - <# } #> - Func<<#= dynamicSelectorTypeParams #>, TRet> selector, - bool isDistinct) - { - <# if (length == 1){ #> - return ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property<#=1#>, false, false, isDistinct).Select(selector); - <# }else{ #> - return Observable.CombineLatest( - <# for(int i=1; i <= length; i++) { #> - ReactiveNotifyPropertyChangedMixin - .SubscribeToExpressionChain(sender, property<#=i#>, false, false, isDistinct), - <# } #> - selector - ); - <# } #> - } - <# } #> - } - - /// A mixin which provides support for subscribing to observable properties. - [RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] - public static class WhenAnyObservableMixin - { - /// Observe a observable which is set to a property, and automatically subscribe to the most recent emitted value. - /// The object where the property chain starts. - /// The first observable to observe. - public static IObservable WhenAnyObservable(this TSender? sender, Expression?>> obs1) - where TSender : class - { - return sender.WhenAny(obs1, x => x.Value!.EmptyIfNull()).Switch(); - } - -<# for(int length=2; length <= maxFuncLength; length++) { #> - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference which ends with an observable. This will be a expression pointing to a end property or field which must be an observable. - <# } #> -<# string paramsStr = String.Join(", ", Enumerable.Range(1, length).Select(x => "Expression?>> obs" + x.ToString())); #> -<# string varsStr = String.Join(", ", Enumerable.Range(1, length).Select(x => "obs" + x.ToString())); #> -<# string valsStr = String.Join(", ", Enumerable.Range(1, length).Select(x => "o" + x.ToString() + ".Value!.EmptyIfNull()")); #> - public static IObservable WhenAnyObservable(this TSender? sender, <#= paramsStr #>) - where TSender : class - { - return sender.WhenAny(<#= varsStr #>, (<#=varsStr.Replace("obs", "o")#>) => new[] {<#= valsStr #>}) - .Select(x => x.Merge()).Switch(); - } -<# } #> - -<# for(int length=2; length <= maxFuncLength; length++) { #> - /// Monitor a property that is an observable, and subscribe to the most recent emitted value. - /// The object where the property chain starts. - <# for(int i=1; i <= length; i++) { #> -/// The <#=i#> property chain to reference. - <# } #>/// The selector which will determine the final value from the properties. This must be an observable. -<# var templParams = Enumerable.Range(1, length).Select(x => "T" + x.ToString() + "?"); #> -<# var templParamsDec = Enumerable.Range(1, length).Select(x => "T" + x.ToString()); #> -<# string varsStr = String.Join(", ", Enumerable.Range(1, length).Select(x => "obs" + x.ToString())); #> -<# string valsStr = String.Join(", ", Enumerable.Range(1, length).Select(x => "o" + x.ToString() + ".Value!.EmptyIfNull()")); #> -<# string selectorTypeParams = String.Join(", ", templParams); #> - public static IObservable WhenAnyObservable>(this TSender? sender, - <# for(int i=1; i <= length; i++) { #> - Expression>?>> obs<#=i#>, - <# } #> - Func<<#= selectorTypeParams #>, TRet> selector) - where TSender : class - { - return sender.WhenAny(<#= varsStr #>, (<#=varsStr.Replace("obs", "o")#>) => Observable.CombineLatest(<#= valsStr #>, selector)) - .Switch(); - } -<# } #> -} - - internal static class ObservableExtensions - { - public static IObservable EmptyIfNull(this IObservable @this) - { - return @this ?? Observable.Empty(); - } - } -} diff --git a/src/ReactiveUI/View/DefaultViewLocator.cs b/src/ReactiveUI/View/DefaultViewLocator.cs index 114aff8981..1cce14aa53 100644 --- a/src/ReactiveUI/View/DefaultViewLocator.cs +++ b/src/ReactiveUI/View/DefaultViewLocator.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -13,7 +13,7 @@ namespace ReactiveUI; /// /// /// -/// This locator uses explicit view-to-viewmodel mappings registered via . +/// This locator uses explicit view-to-viewmodel mappings registered via Map. /// When no mapping is found, it falls back to querying the service locator for IViewFor<TViewModel>. /// /// @@ -37,21 +37,25 @@ namespace ReactiveUI; /// public sealed class DefaultViewLocator : IViewLocator { - // Lock object for synchronizing writes to _mappings. + /// Lock object for synchronizing writes to _mappings. #if NET9_0_OR_GREATER - private readonly System.Threading.Lock _gate = new(); + private readonly Lock _gate = new(); #else private readonly object _gate = new(); #endif - // Cache for MakeGenericType calls in ResolveView(object). - // Key: ViewModelType, Value: IViewFor interface type. + /// + /// Cache for MakeGenericType calls in ResolveView(object). + /// Key: ViewModelType, Value: IViewFor<ViewModelType> interface type. + /// private readonly ConcurrentDictionary _viewForTypeCache = new(); - // Snapshot pattern: Readers access this volatile reference without locking. - // Writers lock, clone, mutate, and swap the reference. - // Keyed by (ViewModelType, Contract). Empty string represents default contract. - private Dictionary<(Type ViewModelType, string Contract), Func> _mappings = new(); + /// + /// Snapshot pattern: Readers access this volatile reference without locking. + /// Writers lock, clone, mutate, and swap the reference. + /// Keyed by (ViewModelType, Contract). Empty string represents default contract. + /// + private Dictionary<(Type ViewModelType, string Contract), Func> _mappings = []; /// /// Initializes a new instance of the class. @@ -60,6 +64,22 @@ internal DefaultViewLocator() { } + /// + /// Registers a direct mapping from a view model type to a view factory using the default contract. + /// + /// View model type. + /// View type that implements IViewFor<TViewModel>. + /// Factory function that creates the view instance. + /// The locator for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public DefaultViewLocator Map(Func factory) + where TViewModel : class + where TView : class, IViewFor => + Map(factory, null); + /// /// Registers a direct mapping from a view model type to a view factory. /// This is the recommended way to register views for AOT-compatible applications. @@ -69,15 +89,11 @@ internal DefaultViewLocator() /// Factory function that creates the view instance. /// Optional contract used to disambiguate multiple views for the same view model. /// The locator for chaining. - /// - /// - /// (() => new LoginView()) - /// .Map(() => new MainView()); - /// ]]> - /// - /// - public DefaultViewLocator Map(Func factory, string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public DefaultViewLocator Map(Func factory, string? contract) where TViewModel : class where TView : class, IViewFor { @@ -88,10 +104,9 @@ public DefaultViewLocator Map(Func factory, string? co lock (_gate) { var current = Volatile.Read(ref _mappings); - var newMappings = new Dictionary<(Type, string), Func>(current) + Dictionary<(Type, string), Func> newMappings = new(current) { - // Covariance of delegates allows Func to be assigned to Func - [key] = factory + [key] = factory }; Interlocked.Exchange(ref _mappings, newMappings); @@ -100,13 +115,30 @@ public DefaultViewLocator Map(Func factory, string? co return this; } + /// + /// Removes the default view mapping for the given view model type. + /// + /// View model type to unmap. + /// The locator for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public DefaultViewLocator Unmap() + where TViewModel : class => + Unmap(null); + /// /// Removes a previously registered view mapping. /// /// View model type to unmap. /// Optional contract to unmap. If null, removes the default mapping. /// The locator for chaining. - public DefaultViewLocator Unmap(string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public DefaultViewLocator Unmap(string? contract) where TViewModel : class { var key = (typeof(TViewModel), contract ?? string.Empty); @@ -114,65 +146,103 @@ public DefaultViewLocator Unmap(string? contract = null) lock (_gate) { var current = Volatile.Read(ref _mappings); - if (current.ContainsKey(key)) + if (!current.ContainsKey(key)) { - var newMappings = new Dictionary<(Type, string), Func>(current); - newMappings.Remove(key); - Interlocked.Exchange(ref _mappings, newMappings); + return this; } + + Dictionary<(Type, string), Func> newMappings = new(current); + newMappings.Remove(key); + Interlocked.Exchange(ref _mappings, newMappings); } return this; } + /// + /// Resolves a view for a view model type using the default contract. Fully AOT-compatible. + /// + /// The view model type to resolve a view for. + /// The resolved view or null when no registration is available. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IViewFor? ResolveView() + where TViewModel : class => + ResolveView(null); + /// /// Resolves a view for a view model type known at compile time. Fully AOT-compatible. /// /// The view model type to resolve a view for. /// Optional contract to disambiguate between multiple views for the same view model. - /// The resolved view or when no registration is available. + /// The resolved view or null when no registration is available. /// /// /// Resolution strategy: /// - /// Check for explicit mapping registered via with the specified contract. + /// Check for explicit mapping registered via Map with the specified contract. /// Query the service locator for IViewFor<TViewModel> with the specified contract. /// /// /// - public IViewFor? ResolveView(string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public IViewFor? ResolveView(string? contract) where TViewModel : class { - // Snapshot read - lock-free - // Volatile.Read ensures we get the latest published dictionary reference. - // The dictionary itself is immutable (copy-on-write), so no further locking is needed. - var mappings = Volatile.Read(ref _mappings); - var vmType = typeof(TViewModel); - var contractKey = contract ?? string.Empty; - - // 1. Check explicit AOT mappings - if (mappings.TryGetValue((vmType, contractKey), out var factory)) + var mappings = Volatile.Read(ref _mappings); + var vmType = typeof(TViewModel); + var contractKey = contract ?? string.Empty; + if (mappings.TryGetValue((vmType, contractKey), out var factory)) { - this.Log().Debug(CultureInfo.InvariantCulture, "Resolved IViewFor<{0}> from explicit mapping", typeof(TViewModel).Name); + this.Log().Debug( + CultureInfo.InvariantCulture, + "Resolved IViewFor<{0}> from explicit mapping", + typeof(TViewModel).Name); return (IViewFor)factory(); } - // 2. Fallback to service locator - var view = AppLocator.Current?.GetService>(contract); - if (view is not null) + var view = AppLocator.Current?.GetService>(contract); + if (view is not null) { - this.Log().Debug(CultureInfo.InvariantCulture, "Resolved IViewFor<{0}> via service locator", typeof(TViewModel).Name); + this.Log().Debug( + CultureInfo.InvariantCulture, + "Resolved IViewFor<{0}> via service locator", + typeof(TViewModel).Name); return view; } - this.Log().Warn(CultureInfo.InvariantCulture, "Failed to resolve view for {0}. Use Map() or register IViewFor in the service locator.", typeof(TViewModel).Name); - return null; + this.Log().Warn( + CultureInfo.InvariantCulture, + "Failed to resolve view for {0}. Use Map() or register IViewFor in the service locator.", + typeof(TViewModel).Name); + return null; } + /// + /// Resolves a view for a view model instance using runtime type information and the default contract. + /// + /// The view model instance to resolve a view for. + /// The resolved view or null when no registration is available. + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, " + + "or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public IViewFor? ResolveView(object? instance) => + ResolveView(instance, null); + /// - [RequiresUnreferencedCode("This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] - [RequiresDynamicCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] - public IViewFor? ResolveView(object? instance, string? contract = null) + [RequiresUnreferencedCode( + "This method uses reflection to determine the view model type at runtime, which may be incompatible with trimming.")] + [RequiresDynamicCode( + "If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, " + + "or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public IViewFor? ResolveView(object? instance, string? contract) { if (instance is null) { @@ -181,34 +251,28 @@ public DefaultViewLocator Unmap(string? contract = null) var vmType = instance.GetType(); var contractKey = contract ?? string.Empty; - - // Snapshot read - lock-free var mappings = Volatile.Read(ref _mappings); - - // 1) Explicit AOT mappings first (fastest, no reflection beyond GetType and Dictionary lookup) if (mappings.TryGetValue((vmType, contractKey), out var factory)) { var view = factory(); - if (view is { } viewFor) + if (view is not { } viewFor) { - viewFor.ViewModel = instance; - return viewFor; + return null; } - return null; + viewFor.ViewModel = instance; + return viewFor; } - // 2) Fallback to service locator via runtime-constructed service type. - // We cache the MakeGenericType result to avoid reflection overhead on subsequent calls. var serviceType = _viewForTypeCache.GetOrAdd(vmType, static t => typeof(IViewFor<>).MakeGenericType(t)); var resolved = AppLocator.Current.GetService(serviceType, contract); - if (resolved is IViewFor resolvedViewFor) + if (resolved is not IViewFor resolvedViewFor) { - resolvedViewFor.ViewModel = instance; - return resolvedViewFor; + return null; } - return null; + resolvedViewFor.ViewModel = instance; + return resolvedViewFor; } } diff --git a/src/ReactiveUI/View/ExcludeFromViewRegistrationAttribute.cs b/src/ReactiveUI/View/ExcludeFromViewRegistrationAttribute.cs index 7e5f50f0d8..3992596460 100644 --- a/src/ReactiveUI/View/ExcludeFromViewRegistrationAttribute.cs +++ b/src/ReactiveUI/View/ExcludeFromViewRegistrationAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/View/SingleInstanceViewAttribute.cs b/src/ReactiveUI/View/SingleInstanceViewAttribute.cs index 69741f75c8..bc784463e5 100644 --- a/src/ReactiveUI/View/SingleInstanceViewAttribute.cs +++ b/src/ReactiveUI/View/SingleInstanceViewAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/View/ViewContractAttribute.cs b/src/ReactiveUI/View/ViewContractAttribute.cs index 4231f92262..c9d363a045 100644 --- a/src/ReactiveUI/View/ViewContractAttribute.cs +++ b/src/ReactiveUI/View/ViewContractAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/View/ViewLocator.cs b/src/ReactiveUI/View/ViewLocator.cs index 11cb41273a..7d9952effe 100644 --- a/src/ReactiveUI/View/ViewLocator.cs +++ b/src/ReactiveUI/View/ViewLocator.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -37,5 +37,6 @@ public static class ViewLocator /// [SuppressMessage("Microsoft.Reliability", "CA1065", Justification = "Exception required to keep interface same.")] public static IViewLocator Current => - AppLocator.Current.GetService() ?? throw new ViewLocatorNotFoundException("Could not find a default ViewLocator. This should never happen, your dependency resolver is broken"); + AppLocator.Current.GetService() ?? throw new ViewLocatorNotFoundException( + "Could not find a default ViewLocator. This should never happen, your dependency resolver is broken"); } diff --git a/src/ReactiveUI/View/ViewLocatorNotFoundException.cs b/src/ReactiveUI/View/ViewLocatorNotFoundException.cs index 3fde0d5df4..00cee937de 100644 --- a/src/ReactiveUI/View/ViewLocatorNotFoundException.cs +++ b/src/ReactiveUI/View/ViewLocatorNotFoundException.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. diff --git a/src/ReactiveUI/View/ViewMappingBuilder.cs b/src/ReactiveUI/View/ViewMappingBuilder.cs index 7b44d83fef..4acb84eb0a 100644 --- a/src/ReactiveUI/View/ViewMappingBuilder.cs +++ b/src/ReactiveUI/View/ViewMappingBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. @@ -10,6 +10,7 @@ namespace ReactiveUI; /// public sealed class ViewMappingBuilder { + /// The underlying view locator that receives the registered mappings. private readonly DefaultViewLocator _locator; /// @@ -22,6 +23,22 @@ internal ViewMappingBuilder(DefaultViewLocator locator) _locator = locator; } + /// + /// Maps a view model type to a view type with automatic instantiation using the default contract. + /// The view must have a parameterless constructor. + /// + /// The view model type. + /// The view type implementing IViewFor<TViewModel>. + /// The builder for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public ViewMappingBuilder Map() + where TViewModel : class + where TView : class, IViewFor, new() => + Map((string?)null); + /// /// Maps a view model type to a view type with automatic instantiation. /// The view must have a parameterless constructor. @@ -30,14 +47,34 @@ internal ViewMappingBuilder(DefaultViewLocator locator) /// The view type implementing IViewFor<TViewModel>. /// Optional contract to disambiguate multiple views for the same view model. /// The builder for chaining. - public ViewMappingBuilder Map(string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public ViewMappingBuilder Map(string? contract) where TViewModel : class where TView : class, IViewFor, new() { - _locator.Map(() => new TView(), contract); + _locator.Map(() => new(), contract); return this; } + /// + /// Maps a view model type to a view type with a custom factory function using the default contract. + /// + /// The view model type. + /// The view type implementing IViewFor<TViewModel>. + /// Factory function that creates the view. + /// The builder for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public ViewMappingBuilder Map(Func factory) + where TViewModel : class + where TView : class, IViewFor => + Map(factory, null); + /// /// Maps a view model type to a view type with a custom factory function. /// Use this when the view requires constructor parameters or custom initialization. @@ -47,7 +84,11 @@ public ViewMappingBuilder Map(string? contract = null) /// Factory function that creates the view. /// Optional contract to disambiguate multiple views for the same view model. /// The builder for chaining. - public ViewMappingBuilder Map(Func factory, string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public ViewMappingBuilder Map(Func factory, string? contract) where TViewModel : class where TView : class, IViewFor { @@ -56,6 +97,21 @@ public ViewMappingBuilder Map(Func factory, string? co return this; } + /// + /// Maps a view model type to a view resolved from the service locator using the default contract. + /// + /// The view model type. + /// The view type implementing IViewFor<TViewModel>. + /// The builder for chaining. + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public ViewMappingBuilder MapFromServiceLocator() + where TViewModel : class + where TView : class, IViewFor => + MapFromServiceLocator(null); + /// /// Maps a view model type to a view resolved from the service locator. /// The view must be registered in the dependency injection container. @@ -64,12 +120,17 @@ public ViewMappingBuilder Map(Func factory, string? co /// The view type implementing IViewFor<TViewModel>. /// Optional contract to disambiguate multiple views for the same view model. /// The builder for chaining. - public ViewMappingBuilder MapFromServiceLocator(string? contract = null) + [SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Generic type parameter is supplied explicitly by the caller by design; it identifies the target type and cannot be inferred from the method's parameters.")] + public ViewMappingBuilder MapFromServiceLocator(string? contract) where TViewModel : class where TView : class, IViewFor { _locator.Map( - () => AppLocator.Current.GetService() ?? throw new InvalidOperationException($"View {typeof(TView).Name} not registered in service locator"), + () => AppLocator.Current.GetService() ?? + throw new InvalidOperationException($"View {typeof(TView).Name} not registered in service locator"), contract); return this; } diff --git a/src/ReactiveUI/WhenAny/ObservableExtensions.cs b/src/ReactiveUI/WhenAny/ObservableExtensions.cs new file mode 100644 index 0000000000..90a922dcc5 --- /dev/null +++ b/src/ReactiveUI/WhenAny/ObservableExtensions.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Internal helpers for observable composition used by the WhenAny mixins. +internal static class ObservableExtensions +{ + /// + /// Returns the source observable, or an empty observable when it is null. + /// + /// The type of the observable's values. + /// The source observable, which may be null. + /// The source observable, or an empty observable when the source is null. + public static IObservable EmptyIfNull(this IObservable @this) => + @this ?? EmptyObservable.Instance; +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity1.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity1.cs new file mode 100644 index 0000000000..72e0d828a5 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity1.cs @@ -0,0 +1,273 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-1 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] +[SuppressMessage( + "Major Code Smell", + "S4018:Generic methods should provide type parameter", + Justification = "Type parameters are supplied explicitly by the caller by design; they identify the observed types and cannot be inferred from the arguments.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes the value of a property, providing an initial value when set up. + /// + /// The type of the source object. + /// The type of the property value. + /// The object whose property is observed. + /// An expression pointing to the property to observe. + /// An observable that emits the property value and subsequent changes. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1) => + sender!.WhenAny(property1, c1 => c1.Value); + + /// + /// AOT-friendly overload that observes a property by name instead of an expression. + /// + /// The type of the source object. + /// The type of the property value. + /// The object whose property is observed. + /// The name of the property to observe. + /// An observable that emits the property value and subsequent changes. + public static IObservable WhenAnyValue( + this TSender? sender, + string propertyName) => + new WhenAnyValueSink( + sender!.ObservableForProperty(propertyName, beforeChange: false, skipInitial: false, isDistinct: true), + static x => x); + + /// + /// Observes the value of a property, optionally emitting only distinct values. + /// + /// The type of the source object. + /// The type of the property value. + /// The object whose property is observed. + /// An expression pointing to the property to observe. + /// Whether to emit only when the combined value changes. + /// An observable that emits the property value and subsequent changes. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + bool isDistinct) => + sender!.WhenAny(property1, c1 => c1.Value, isDistinct); + + /// + /// AOT-friendly overload that observes a property by name, optionally distinct. + /// + /// The type of the source object. + /// The type of the property value. + /// The object whose property is observed. + /// The name of the property to observe. + /// Whether to emit only when the combined value changes. + /// An observable that emits the property value and subsequent changes. + public static IObservable WhenAnyValue( + this TSender? sender, + string propertyName, + bool isDistinct) => + new WhenAnyValueSink( + sender!.ObservableForProperty(propertyName, beforeChange: false, skipInitial: false, isDistinct: isDistinct), + static x => x); + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Func selector) => + sender!.WhenAny( + property1, + c1 => selector(c1.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// The name of property 1. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + c1 => selector(c1.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// The name of property 1. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Func, TRet> selector) => + new WhenAnyChangeSink, TRet>( + sender!.ObservableForProperty(property1, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// The name of property 1. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + Func, TRet> selector) => + new WhenAnyChangeSink, TRet>( + sender!.ObservableForProperty(property1Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Func, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The object whose properties are observed. + /// The name of property 1. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + Func, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Func, TRet> selector) => + new WhenAnyChangeSink, TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Func, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity10.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity10.cs new file mode 100644 index 0000000000..4e25b077ce --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity10.cs @@ -0,0 +1,704 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-10 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + property10, + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + o10, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + property10, + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + o10, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + sender!.ObservableForProperty(property6, false, false), + sender!.ObservableForProperty(property7, false, false), + sender!.ObservableForProperty(property8, false, false), + sender!.ObservableForProperty(property9, false, false), + sender!.ObservableForProperty(property10, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + sender!.ObservableForProperty(property6Name, false, false), + sender!.ObservableForProperty(property7Name, false, false), + sender!.ObservableForProperty(property8Name, false, false), + sender!.ObservableForProperty(property9Name, false, false), + sender!.ObservableForProperty(property10Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + sender!.ObservableForProperty(property6, false, false, isDistinct), + sender!.ObservableForProperty(property7, false, false, isDistinct), + sender!.ObservableForProperty(property8, false, false, isDistinct), + sender!.ObservableForProperty(property9, false, false, isDistinct), + sender!.ObservableForProperty(property10, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + sender!.ObservableForProperty(property6Name, false, false, isDistinct), + sender!.ObservableForProperty(property7Name, false, false, isDistinct), + sender!.ObservableForProperty(property8Name, false, false, isDistinct), + sender!.ObservableForProperty(property9Name, false, false, isDistinct), + sender!.ObservableForProperty(property10Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Expression? property10, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + sender.SubscribeToExpressionChain(property6, false, false), + sender.SubscribeToExpressionChain(property7, false, false), + sender.SubscribeToExpressionChain(property8, false, false), + sender.SubscribeToExpressionChain(property9, false, false), + sender.SubscribeToExpressionChain(property10, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Expression? property10, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + sender.SubscribeToExpressionChain(property6, false, false, isDistinct), + sender.SubscribeToExpressionChain(property7, false, false, isDistinct), + sender.SubscribeToExpressionChain(property8, false, false, isDistinct), + sender.SubscribeToExpressionChain(property9, false, false, isDistinct), + sender.SubscribeToExpressionChain(property10, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity11.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity11.cs new file mode 100644 index 0000000000..541a3cfe3f --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity11.cs @@ -0,0 +1,767 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-11 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + property10, + property11, + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value, c10.Value, c11.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + o10, + o11, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + property10, + property11, + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) => selector( + c1.Value, + c2.Value, + c3.Value, + c4.Value, + c5.Value, + c6.Value, + c7.Value, + c8.Value, + c9.Value, + c10.Value, + c11.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + o10, + o11, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + sender!.ObservableForProperty(property6, false, false), + sender!.ObservableForProperty(property7, false, false), + sender!.ObservableForProperty(property8, false, false), + sender!.ObservableForProperty(property9, false, false), + sender!.ObservableForProperty(property10, false, false), + sender!.ObservableForProperty(property11, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + sender!.ObservableForProperty(property6Name, false, false), + sender!.ObservableForProperty(property7Name, false, false), + sender!.ObservableForProperty(property8Name, false, false), + sender!.ObservableForProperty(property9Name, false, false), + sender!.ObservableForProperty(property10Name, false, false), + sender!.ObservableForProperty(property11Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + sender!.ObservableForProperty(property6, false, false, isDistinct), + sender!.ObservableForProperty(property7, false, false, isDistinct), + sender!.ObservableForProperty(property8, false, false, isDistinct), + sender!.ObservableForProperty(property9, false, false, isDistinct), + sender!.ObservableForProperty(property10, false, false, isDistinct), + sender!.ObservableForProperty(property11, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + sender!.ObservableForProperty(property6Name, false, false, isDistinct), + sender!.ObservableForProperty(property7Name, false, false, isDistinct), + sender!.ObservableForProperty(property8Name, false, false, isDistinct), + sender!.ObservableForProperty(property9Name, false, false, isDistinct), + sender!.ObservableForProperty(property10Name, false, false, isDistinct), + sender!.ObservableForProperty(property11Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Expression? property10, + Expression? property11, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + sender.SubscribeToExpressionChain(property6, false, false), + sender.SubscribeToExpressionChain(property7, false, false), + sender.SubscribeToExpressionChain(property8, false, false), + sender.SubscribeToExpressionChain(property9, false, false), + sender.SubscribeToExpressionChain(property10, false, false), + sender.SubscribeToExpressionChain(property11, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Expression? property10, + Expression? property11, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + sender.SubscribeToExpressionChain(property6, false, false, isDistinct), + sender.SubscribeToExpressionChain(property7, false, false, isDistinct), + sender.SubscribeToExpressionChain(property8, false, false, isDistinct), + sender.SubscribeToExpressionChain(property9, false, false, isDistinct), + sender.SubscribeToExpressionChain(property10, false, false, isDistinct), + sender.SubscribeToExpressionChain(property11, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity12.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity12.cs new file mode 100644 index 0000000000..8bbd7f86af --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity12.cs @@ -0,0 +1,832 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-12 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// An expression pointing to property 12. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Expression> property12, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + property10, + property11, + property12, + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) => selector( + c1.Value, + c2.Value, + c3.Value, + c4.Value, + c5.Value, + c6.Value, + c7.Value, + c8.Value, + c9.Value, + c10.Value, + c11.Value, + c12.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// The name of property 12. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + string property12Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o12 = sender!.ObservableForProperty(property12Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + o10, + o11, + o12, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// An expression pointing to property 12. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Expression> property12, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + property10, + property11, + property12, + (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) => selector( + c1.Value, + c2.Value, + c3.Value, + c4.Value, + c5.Value, + c6.Value, + c7.Value, + c8.Value, + c9.Value, + c10.Value, + c11.Value, + c12.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// The name of property 12. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + string property12Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o10 = sender!.ObservableForProperty(property10Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o11 = sender!.ObservableForProperty(property11Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o12 = sender!.ObservableForProperty(property12Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + o10, + o11, + o12, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// An expression pointing to property 12. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Expression> property12, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + sender!.ObservableForProperty(property6, false, false), + sender!.ObservableForProperty(property7, false, false), + sender!.ObservableForProperty(property8, false, false), + sender!.ObservableForProperty(property9, false, false), + sender!.ObservableForProperty(property10, false, false), + sender!.ObservableForProperty(property11, false, false), + sender!.ObservableForProperty(property12, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// The name of property 12. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + string property12Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + sender!.ObservableForProperty(property6Name, false, false), + sender!.ObservableForProperty(property7Name, false, false), + sender!.ObservableForProperty(property8Name, false, false), + sender!.ObservableForProperty(property9Name, false, false), + sender!.ObservableForProperty(property10Name, false, false), + sender!.ObservableForProperty(property11Name, false, false), + sender!.ObservableForProperty(property12Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// An expression pointing to property 12. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Expression> property10, + Expression> property11, + Expression> property12, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + sender!.ObservableForProperty(property6, false, false, isDistinct), + sender!.ObservableForProperty(property7, false, false, isDistinct), + sender!.ObservableForProperty(property8, false, false, isDistinct), + sender!.ObservableForProperty(property9, false, false, isDistinct), + sender!.ObservableForProperty(property10, false, false, isDistinct), + sender!.ObservableForProperty(property11, false, false, isDistinct), + sender!.ObservableForProperty(property12, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// The name of property 10. + /// The name of property 11. + /// The name of property 12. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + string property10Name, + string property11Name, + string property12Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + sender!.ObservableForProperty(property6Name, false, false, isDistinct), + sender!.ObservableForProperty(property7Name, false, false, isDistinct), + sender!.ObservableForProperty(property8Name, false, false, isDistinct), + sender!.ObservableForProperty(property9Name, false, false, isDistinct), + sender!.ObservableForProperty(property10Name, false, false, isDistinct), + sender!.ObservableForProperty(property11Name, false, false, isDistinct), + sender!.ObservableForProperty(property12Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// An expression pointing to property 12. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Expression? property10, + Expression? property11, + Expression? property12, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + sender.SubscribeToExpressionChain(property6, false, false), + sender.SubscribeToExpressionChain(property7, false, false), + sender.SubscribeToExpressionChain(property8, false, false), + sender.SubscribeToExpressionChain(property9, false, false), + sender.SubscribeToExpressionChain(property10, false, false), + sender.SubscribeToExpressionChain(property11, false, false), + sender.SubscribeToExpressionChain(property12, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// An expression pointing to property 10. + /// An expression pointing to property 11. + /// An expression pointing to property 12. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Expression? property10, + Expression? property11, + Expression? property12, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + sender.SubscribeToExpressionChain(property6, false, false, isDistinct), + sender.SubscribeToExpressionChain(property7, false, false, isDistinct), + sender.SubscribeToExpressionChain(property8, false, false, isDistinct), + sender.SubscribeToExpressionChain(property9, false, false, isDistinct), + sender.SubscribeToExpressionChain(property10, false, false, isDistinct), + sender.SubscribeToExpressionChain(property11, false, false, isDistinct), + sender.SubscribeToExpressionChain(property12, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity2.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity2.cs new file mode 100644 index 0000000000..72dd21ff36 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity2.cs @@ -0,0 +1,337 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-2 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2) => + sender!.WhenAny( + property1, + property2, + (c1, c2) => (c1.Value, c2.Value)); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + static (v1, v2) => (v1, v2)); + } + + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + (c1, c2) => (c1.Value, c2.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + static (v1, v2) => (v1, v2)); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Func selector) => + sender!.WhenAny( + property1, + property2, + (c1, c2) => selector(c1.Value, c2.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + (c1, c2) => selector(c1.Value, c2.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Func, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + Func, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Func, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + Func, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Func, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Func, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity3.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity3.cs new file mode 100644 index 0000000000..a2c3aa8d2b --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity3.cs @@ -0,0 +1,395 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-3 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3) => + sender!.WhenAny( + property1, + property2, + property3, + (c1, c2, c3) => (c1.Value, c2.Value, c3.Value)); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + static (v1, v2, v3) => (v1, v2, v3)); + } + + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + (c1, c2, c3) => (c1.Value, c2.Value, c3.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + static (v1, v2, v3) => (v1, v2, v3)); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + (c1, c2, c3) => selector(c1.Value, c2.Value, c3.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + (c1, c2, c3) => selector(c1.Value, c2.Value, c3.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Func, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + Func, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Func, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + Func, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Func, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Func, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity4.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity4.cs new file mode 100644 index 0000000000..0c10520d96 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity4.cs @@ -0,0 +1,453 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-4 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + (c1, c2, c3, c4) => (c1.Value, c2.Value, c3.Value, c4.Value)); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + static (v1, v2, v3, v4) => (v1, v2, v3, v4)); + } + + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + (c1, c2, c3, c4) => (c1.Value, c2.Value, c3.Value, c4.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + static (v1, v2, v3, v4) => (v1, v2, v3, v4)); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + (c1, c2, c3, c4) => selector(c1.Value, c2.Value, c3.Value, c4.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + (c1, c2, c3, c4) => selector(c1.Value, c2.Value, c3.Value, c4.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, IObservedChange, TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, IObservedChange, TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Func, IObservedChange, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink, IObservedChange, IObservedChange, IObservedChange, TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity5.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity5.cs new file mode 100644 index 0000000000..d96c4d5427 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity5.cs @@ -0,0 +1,563 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-5 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + (c1, c2, c3, c4, c5) => (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value)); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + static (v1, v2, v3, v4, v5) => (v1, v2, v3, v4, v5)); + } + + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + (c1, c2, c3, c4, c5) => (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + static (v1, v2, v3, v4, v5) => (v1, v2, v3, v4, v5)); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + (c1, c2, c3, c4, c5) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + (c1, c2, c3, c4, c5) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + Func, IObservedChange, IObservedChange, IObservedChange, IObservedChange, TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity6.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity6.cs new file mode 100644 index 0000000000..1912be38ed --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity6.cs @@ -0,0 +1,657 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-6 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + (c1, c2, c3, c4, c5, c6) => (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value)); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + static (v1, v2, v3, v4, v5, v6) => (v1, v2, v3, v4, v5, v6)); + } + + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + (c1, c2, c3, c4, c5, c6) => (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + static (v1, v2, v3, v4, v5, v6) => (v1, v2, v3, v4, v5, v6)); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + (c1, c2, c3, c4, c5, c6) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + (c1, c2, c3, c4, c5, c6) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + sender!.ObservableForProperty(property6, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + sender!.ObservableForProperty(property6Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + sender!.ObservableForProperty(property6, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + sender!.ObservableForProperty(property6Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + sender.SubscribeToExpressionChain(property6, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + sender.SubscribeToExpressionChain(property6, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity7.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity7.cs new file mode 100644 index 0000000000..e0d7ec6682 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity7.cs @@ -0,0 +1,727 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-7 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6, T7 Value7)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + (c1, c2, c3, c4, c5, c6, c7) => (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value)); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6, T7 Value7)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + static (v1, v2, v3, v4, v5, v6, v7) => (v1, v2, v3, v4, v5, v6, v7)); + } + + /// + /// Observes several properties and projects their values into a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6, T7 Value7)> WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + (c1, c2, c3, c4, c5, c6, c7) => (c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and projects a tuple. + /// + /// The type of the source object. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// Whether to emit only when the combined value changes. + /// An observable that emits a tuple of the observed property values. + public static IObservable<(T1 Value1, T2 Value2, T3 Value3, T4 Value4, T5 Value5, T6 Value6, T7 Value7)> WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + static (v1, v2, v3, v4, v5, v6, v7) => (v1, v2, v3, v4, v5, v6, v7)); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + (c1, c2, c3, c4, c5, c6, c7) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + (c1, c2, c3, c4, c5, c6, c7) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + sender!.ObservableForProperty(property6, false, false), + sender!.ObservableForProperty(property7, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + sender!.ObservableForProperty(property6Name, false, false), + sender!.ObservableForProperty(property7Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + sender!.ObservableForProperty(property6, false, false, isDistinct), + sender!.ObservableForProperty(property7, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + sender!.ObservableForProperty(property6Name, false, false, isDistinct), + sender!.ObservableForProperty(property7Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + sender.SubscribeToExpressionChain(property6, false, false), + sender.SubscribeToExpressionChain(property7, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + sender.SubscribeToExpressionChain(property6, false, false, isDistinct), + sender.SubscribeToExpressionChain(property7, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity8.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity8.cs new file mode 100644 index 0000000000..962b6c137c --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity8.cs @@ -0,0 +1,600 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-8 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + (c1, c2, c3, c4, c5, c6, c7, c8) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + (c1, c2, c3, c4, c5, c6, c7, c8) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + sender!.ObservableForProperty(property6, false, false), + sender!.ObservableForProperty(property7, false, false), + sender!.ObservableForProperty(property8, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + sender!.ObservableForProperty(property6Name, false, false), + sender!.ObservableForProperty(property7Name, false, false), + sender!.ObservableForProperty(property8Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + sender!.ObservableForProperty(property6, false, false, isDistinct), + sender!.ObservableForProperty(property7, false, false, isDistinct), + sender!.ObservableForProperty(property8, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + sender!.ObservableForProperty(property6Name, false, false, isDistinct), + sender!.ObservableForProperty(property7Name, false, false, isDistinct), + sender!.ObservableForProperty(property8Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + sender.SubscribeToExpressionChain(property6, false, false), + sender.SubscribeToExpressionChain(property7, false, false), + sender.SubscribeToExpressionChain(property8, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + sender.SubscribeToExpressionChain(property6, false, false, isDistinct), + sender.SubscribeToExpressionChain(property7, false, false, isDistinct), + sender.SubscribeToExpressionChain(property8, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity9.cs b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity9.cs new file mode 100644 index 0000000000..9b9e8e2f25 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyMixin.Arity9.cs @@ -0,0 +1,652 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-9 WhenAny / WhenAnyValue / WhenAnyDynamic extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyMixin +{ + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Func selector) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + (c1, c2, c3, c4, c5, c6, c7, c8, c9) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value)); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// Combines the observed property values into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + Func selector) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: true); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: true); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + selector); + } + + /// + /// Observes several properties and combines their values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Func selector, + bool isDistinct) => + sender!.WhenAny( + property1, + property2, + property3, + property4, + property5, + property6, + property7, + property8, + property9, + (c1, c2, c3, c4, c5, c6, c7, c8, c9) => selector(c1.Value, c2.Value, c3.Value, c4.Value, c5.Value, c6.Value, c7.Value, c8.Value, c9.Value), + isDistinct); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// Combines the observed property values into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyValue( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + Func selector, + bool isDistinct) + { + var o1 = sender!.ObservableForProperty(property1Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o2 = sender!.ObservableForProperty(property2Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o3 = sender!.ObservableForProperty(property3Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o4 = sender!.ObservableForProperty(property4Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o5 = sender!.ObservableForProperty(property5Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o6 = sender!.ObservableForProperty(property6Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o7 = sender!.ObservableForProperty(property7Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o8 = sender!.ObservableForProperty(property8Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + var o9 = sender!.ObservableForProperty(property9Name, beforeChange: false, skipInitial: false, isDistinct: isDistinct); + return new WhenAnyValueSink( + o1, + o2, + o3, + o4, + o5, + o6, + o7, + o8, + o9, + selector); + } + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false), + sender!.ObservableForProperty(property2, false, false), + sender!.ObservableForProperty(property3, false, false), + sender!.ObservableForProperty(property4, false, false), + sender!.ObservableForProperty(property5, false, false), + sender!.ObservableForProperty(property6, false, false), + sender!.ObservableForProperty(property7, false, false), + sender!.ObservableForProperty(property8, false, false), + sender!.ObservableForProperty(property9, false, false), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false), + sender!.ObservableForProperty(property2Name, false, false), + sender!.ObservableForProperty(property3Name, false, false), + sender!.ObservableForProperty(property4Name, false, false), + sender!.ObservableForProperty(property5Name, false, false), + sender!.ObservableForProperty(property6Name, false, false), + sender!.ObservableForProperty(property7Name, false, false), + sender!.ObservableForProperty(property8Name, false, false), + sender!.ObservableForProperty(property9Name, false, false), + selector); + + /// + /// Observes several properties and combines their change notifications with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + Expression> property1, + Expression> property2, + Expression> property3, + Expression> property4, + Expression> property5, + Expression> property6, + Expression> property7, + Expression> property8, + Expression> property9, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1, false, false, isDistinct), + sender!.ObservableForProperty(property2, false, false, isDistinct), + sender!.ObservableForProperty(property3, false, false, isDistinct), + sender!.ObservableForProperty(property4, false, false, isDistinct), + sender!.ObservableForProperty(property5, false, false, isDistinct), + sender!.ObservableForProperty(property6, false, false, isDistinct), + sender!.ObservableForProperty(property7, false, false, isDistinct), + sender!.ObservableForProperty(property8, false, false, isDistinct), + sender!.ObservableForProperty(property9, false, false, isDistinct), + selector); + + /// + /// AOT-friendly overload that observes properties by name and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose properties are observed. + /// The name of property 1. + /// The name of property 2. + /// The name of property 3. + /// The name of property 4. + /// The name of property 5. + /// The name of property 6. + /// The name of property 7. + /// The name of property 8. + /// The name of property 9. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAny( + this TSender? sender, + string property1Name, + string property2Name, + string property3Name, + string property4Name, + string property5Name, + string property6Name, + string property7Name, + string property8Name, + string property9Name, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender!.ObservableForProperty(property1Name, false, false, isDistinct), + sender!.ObservableForProperty(property2Name, false, false, isDistinct), + sender!.ObservableForProperty(property3Name, false, false, isDistinct), + sender!.ObservableForProperty(property4Name, false, false, isDistinct), + sender!.ObservableForProperty(property5Name, false, false, isDistinct), + sender!.ObservableForProperty(property6Name, false, false, isDistinct), + sender!.ObservableForProperty(property7Name, false, false, isDistinct), + sender!.ObservableForProperty(property8Name, false, false, isDistinct), + sender!.ObservableForProperty(property9Name, false, false, isDistinct), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// Combines the observed change notifications into a result. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false), + sender.SubscribeToExpressionChain(property2, false, false), + sender.SubscribeToExpressionChain(property3, false, false), + sender.SubscribeToExpressionChain(property4, false, false), + sender.SubscribeToExpressionChain(property5, false, false), + sender.SubscribeToExpressionChain(property6, false, false), + sender.SubscribeToExpressionChain(property7, false, false), + sender.SubscribeToExpressionChain(property8, false, false), + sender.SubscribeToExpressionChain(property9, false, false), + selector); + + /// + /// Observes several dynamically-typed property chains and combines them with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The object whose properties are observed. + /// An expression pointing to property 1. + /// An expression pointing to property 2. + /// An expression pointing to property 3. + /// An expression pointing to property 4. + /// An expression pointing to property 5. + /// An expression pointing to property 6. + /// An expression pointing to property 7. + /// An expression pointing to property 8. + /// An expression pointing to property 9. + /// Combines the observed change notifications into a result. + /// Whether to emit only when the combined value changes. + /// An observable that emits the projected result on each change. + public static IObservable WhenAnyDynamic( + this TSender? sender, + Expression? property1, + Expression? property2, + Expression? property3, + Expression? property4, + Expression? property5, + Expression? property6, + Expression? property7, + Expression? property8, + Expression? property9, + Func< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet> selector, + bool isDistinct) => + new WhenAnyChangeSink< + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + IObservedChange, + TRet>( + sender.SubscribeToExpressionChain(property1, false, false, isDistinct), + sender.SubscribeToExpressionChain(property2, false, false, isDistinct), + sender.SubscribeToExpressionChain(property3, false, false, isDistinct), + sender.SubscribeToExpressionChain(property4, false, false, isDistinct), + sender.SubscribeToExpressionChain(property5, false, false, isDistinct), + sender.SubscribeToExpressionChain(property6, false, false, isDistinct), + sender.SubscribeToExpressionChain(property7, false, false, isDistinct), + sender.SubscribeToExpressionChain(property8, false, false, isDistinct), + sender.SubscribeToExpressionChain(property9, false, false, isDistinct), + selector); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity1.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity1.cs new file mode 100644 index 0000000000..5b26b546e0 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity1.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-1 WhenAnyObservable extension overloads. +[RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes a property that is itself an observable and subscribes to its latest value. + /// + /// The type of the source object. + /// The type of the observable's value. + /// The object whose observable property is observed. + /// An expression pointing to the observable property. + /// An observable that produces the latest value of the observed observable. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny(obs1, static x => x.Value!.EmptyIfNull())); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity10.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity10.cs new file mode 100644 index 0000000000..389b5b2d9e --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity10.cs @@ -0,0 +1,142 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-10 WhenAnyObservable extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// An expression pointing to observable property 10. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9, + Expression?>> obs10) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + obs10, + (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + o10.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// An expression pointing to observable property 10. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9, + Expression?>> obs10, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + obs10, + (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + o10.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity11.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity11.cs new file mode 100644 index 0000000000..8436bdaed3 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity11.cs @@ -0,0 +1,151 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-11 WhenAnyObservable extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// An expression pointing to observable property 10. + /// An expression pointing to observable property 11. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9, + Expression?>> obs10, + Expression?>> obs11) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + obs10, + obs11, + (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + o10.Value!.EmptyIfNull(), + o11.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// An expression pointing to observable property 10. + /// An expression pointing to observable property 11. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9, + Expression?>> obs10, + Expression?>> obs11, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + obs10, + obs11, + (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + o10.Value!.EmptyIfNull(), + o11.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity12.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity12.cs new file mode 100644 index 0000000000..645275e214 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity12.cs @@ -0,0 +1,160 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-12 WhenAnyObservable extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// An expression pointing to observable property 10. + /// An expression pointing to observable property 11. + /// An expression pointing to observable property 12. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9, + Expression?>> obs10, + Expression?>> obs11, + Expression?>> obs12) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + obs10, + obs11, + obs12, + (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + o10.Value!.EmptyIfNull(), + o11.Value!.EmptyIfNull(), + o12.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The type of property 10. + /// The type of property 11. + /// The type of property 12. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// An expression pointing to observable property 10. + /// An expression pointing to observable property 11. + /// An expression pointing to observable property 12. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9, + Expression?>> obs10, + Expression?>> obs11, + Expression?>> obs12, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + obs10, + obs11, + obs12, + (o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + o10.Value!.EmptyIfNull(), + o11.Value!.EmptyIfNull(), + o12.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity2.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity2.cs new file mode 100644 index 0000000000..317cc803c3 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity2.cs @@ -0,0 +1,66 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-2 WhenAnyObservable extension overloads. +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + (o1, o2) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + (o1, o2) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity3.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity3.cs new file mode 100644 index 0000000000..b58b8cfc81 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity3.cs @@ -0,0 +1,75 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-3 WhenAnyObservable extension overloads. +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + (o1, o2, o3) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + (o1, o2, o3) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity4.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity4.cs new file mode 100644 index 0000000000..481280658d --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity4.cs @@ -0,0 +1,84 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-4 WhenAnyObservable extension overloads. +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + (o1, o2, o3, o4) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + (o1, o2, o3, o4) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity5.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity5.cs new file mode 100644 index 0000000000..57b401807b --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity5.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-5 WhenAnyObservable extension overloads. +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + (o1, o2, o3, o4, o5) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + (o1, o2, o3, o4, o5) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity6.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity6.cs new file mode 100644 index 0000000000..1de2825e56 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity6.cs @@ -0,0 +1,106 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-6 WhenAnyObservable extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + (o1, o2, o3, o4, o5, o6) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + (o1, o2, o3, o4, o5, o6) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity7.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity7.cs new file mode 100644 index 0000000000..61c232924f --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity7.cs @@ -0,0 +1,115 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-7 WhenAnyObservable extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + (o1, o2, o3, o4, o5, o6, o7) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + (o1, o2, o3, o4, o5, o6, o7) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity8.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity8.cs new file mode 100644 index 0000000000..64f4e0f41e --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity8.cs @@ -0,0 +1,124 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-8 WhenAnyObservable extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + (o1, o2, o3, o4, o5, o6, o7, o8) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + (o1, o2, o3, o4, o5, o6, o7, o8) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity9.cs b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity9.cs new file mode 100644 index 0000000000..37b802d608 --- /dev/null +++ b/src/ReactiveUI/WhenAny/WhenAnyObservableMixin.Arity9.cs @@ -0,0 +1,133 @@ +// Copyright (c) 2009-2026 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq.Expressions; +using ReactiveUI.Internal; + +namespace ReactiveUI; + +/// Provides the arity-9 WhenAnyObservable extension overloads. +[SuppressMessage( + "Major Code Smell", + "S107:Methods should not have too many parameters", + Justification = "Arity-N variadic overloads intentionally expose more than seven parameters.")] +public static partial class WhenAnyObservableMixin +{ + /// + /// Observes several observable-valued properties and merges their latest values. + /// + /// The type of the source object. + /// The type of the observables' value. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// An observable that produces the merged latest values of the observed observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9) + where TSender : class + { + return new WhenAnyObservableMergeSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + (o1, o2, o3, o4, o5, o6, o7, o8, o9) => new[] + { + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + })); + } + + /// + /// Observes several observable-valued properties and combines their latest values with a selector. + /// + /// The type of the source object. + /// The type of the resulting value. + /// The type of property 1. + /// The type of property 2. + /// The type of property 3. + /// The type of property 4. + /// The type of property 5. + /// The type of property 6. + /// The type of property 7. + /// The type of property 8. + /// The type of property 9. + /// The object whose observable properties are observed. + /// An expression pointing to observable property 1. + /// An expression pointing to observable property 2. + /// An expression pointing to observable property 3. + /// An expression pointing to observable property 4. + /// An expression pointing to observable property 5. + /// An expression pointing to observable property 6. + /// An expression pointing to observable property 7. + /// An expression pointing to observable property 8. + /// An expression pointing to observable property 9. + /// Combines the latest values of the observed observables into a result. + /// An observable that produces the projected result of the combined observables. + public static IObservable WhenAnyObservable( + this TSender? sender, + Expression?>> obs1, + Expression?>> obs2, + Expression?>> obs3, + Expression?>> obs4, + Expression?>> obs5, + Expression?>> obs6, + Expression?>> obs7, + Expression?>> obs8, + Expression?>> obs9, + Func selector) + where TSender : class => + new WhenAnyObservableSwitchSink( + sender.WhenAny( + obs1, + obs2, + obs3, + obs4, + obs5, + obs6, + obs7, + obs8, + obs9, + (o1, o2, o3, o4, o5, o6, o7, o8, o9) => (IObservable)new WhenAnyChangeSink( + o1.Value!.EmptyIfNull(), + o2.Value!.EmptyIfNull(), + o3.Value!.EmptyIfNull(), + o4.Value!.EmptyIfNull(), + o5.Value!.EmptyIfNull(), + o6.Value!.EmptyIfNull(), + o7.Value!.EmptyIfNull(), + o8.Value!.EmptyIfNull(), + o9.Value!.EmptyIfNull(), + selector))); +} diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/Components/App.razor b/src/examples/ReactiveUI.Builder.BlazorServer/Components/App.razor index 85435faf09..c534e0307b 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/Components/App.razor +++ b/src/examples/ReactiveUI.Builder.BlazorServer/Components/App.razor @@ -2,22 +2,22 @@ - - - - - - - - - + + + + + + + + + - - - + + + diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor b/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor index eb5dbc884c..7ec5401c16 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor +++ b/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor @@ -4,7 +4,7 @@ @inherits ReactiveLayoutComponentBase
-@*
- +
diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor.css b/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor.css index 38d1f25983..0847203875 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/MainLayout.razor.css @@ -21,20 +21,20 @@ main { align-items: center; } - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } +.top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; +} - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } +.top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; +} - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } +.top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; +} @media (max-width: 640.98px) { .top-row { @@ -90,9 +90,9 @@ main { z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/NavMenu.razor b/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/NavMenu.razor index b2415bc090..c0637e0bca 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/NavMenu.razor +++ b/src/examples/ReactiveUI.Builder.BlazorServer/Components/Layout/NavMenu.razor @@ -4,7 +4,7 @@ - +
+public partial class ChatRoomView : ReactiveComponentBase { /// - /// Represents a view component for displaying and interacting with a chat room in the user interface. + /// Handles the send button click by executing the view model's send command. /// - public partial class ChatRoomView : ReactiveComponentBase + /// The mouse event arguments raised by the button click. + /// A task that represents the asynchronous send operation. + private async Task OnSendClicked(MouseEventArgs args) { - private async Task OnSendClicked(MouseEventArgs args) + if (ViewModel is null) { - if (ViewModel is null) - { - throw new ArgumentNullException(nameof(ViewModel)); - } - - await ViewModel.SendMessage.Execute(); + throw new ArgumentNullException(nameof(ViewModel)); } - private async Task OnNavigateBack(MouseEventArgs args) - { - if (ViewModel is null) - { - throw new ArgumentNullException(nameof(ViewModel)); - } + await ViewModel.SendMessage.Execute(); + } - await ViewModel.NavigateBack.Execute(); + /// + /// Handles the back button click by executing the view model's navigate-back command. + /// + /// The mouse event arguments raised by the button click. + /// A task that represents the asynchronous navigation operation. + private async Task OnNavigateBack(MouseEventArgs args) + { + if (ViewModel is null) + { + throw new ArgumentNullException(nameof(ViewModel)); } + + await ViewModel.NavigateBack.Execute(); } } diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/Views/LobbyView.razor b/src/examples/ReactiveUI.Builder.BlazorServer/Views/LobbyView.razor index 8da6e0d83e..9377a2ab93 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/Views/LobbyView.razor +++ b/src/examples/ReactiveUI.Builder.BlazorServer/Views/LobbyView.razor @@ -12,7 +12,7 @@ + @bind-value="ViewModel?.DisplayName"/> @@ -20,7 +20,7 @@ + @bind-value="ViewModel?.RoomName"/>
+public partial class LobbyView : ReactiveComponentBase { /// - /// Lobby (rooms listing) view. + /// Handles the create-room button click by executing the view model's create command. /// - public partial class LobbyView : ReactiveComponentBase + /// The mouse event arguments raised by the button click. + /// A task that represents the asynchronous create operation. + private async Task OnCreateRoomClicked(MouseEventArgs args) { - private async Task OnCreateRoomClicked(MouseEventArgs args) + if (ViewModel is null) { - if (ViewModel is null) - { - throw new ArgumentNullException(nameof(ViewModel)); - } - - await ViewModel.CreateRoom.Execute(); + throw new ArgumentNullException(nameof(ViewModel)); } - private async Task OnDeleteSelectedClicked(MouseEventArgs args) - { - if (ViewModel is null) - { - throw new ArgumentNullException(nameof(ViewModel)); - } - - if (ViewModel.SelectedChatRoom is null) - { - throw new InvalidOperationException("No chat room selected to delete."); - } + await ViewModel.CreateRoom.Execute(); + } - await ViewModel.DeleteRoom.Execute(ViewModel.SelectedChatRoom); + /// + /// Handles the delete button click by executing the view model's delete command for the selected room. + /// + /// The mouse event arguments raised by the button click. + /// A task that represents the asynchronous delete operation. + private async Task OnDeleteSelectedClicked(MouseEventArgs args) + { + if (ViewModel is null) + { + throw new ArgumentNullException(nameof(ViewModel)); } - private void OnRoomClicked(ChatRoom room) + if (ViewModel.SelectedChatRoom is null) { - if (ViewModel is null) - { - throw new ArgumentNullException(nameof(ViewModel)); - } - - ViewModel.SelectedChatRoom = room; + throw new InvalidOperationException("No chat room selected to delete."); } - private async Task OnRoomDoubleClicked(ChatRoom room) + await ViewModel.DeleteRoom.Execute(ViewModel.SelectedChatRoom); + } + + /// + /// Handles a single click on a room by marking it as the currently selected room. + /// + /// The room that was clicked. + private void OnRoomClicked(ChatRoom room) + { + if (ViewModel is null) { - if (ViewModel is null) - { - throw new ArgumentNullException(nameof(ViewModel)); - } + throw new ArgumentNullException(nameof(ViewModel)); + } + + ViewModel.SelectedChatRoom = room; + } - await ViewModel.JoinRoom.Execute(room); + /// + /// Handles a double click on a room by executing the view model's join command to enter it. + /// + /// The room that was double clicked. + /// A task that represents the asynchronous join operation. + private async Task OnRoomDoubleClicked(ChatRoom room) + { + if (ViewModel is null) + { + throw new ArgumentNullException(nameof(ViewModel)); } + + await ViewModel.JoinRoom.Execute(room); } } diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.Development.json b/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.Development.json index 0c208ae918..a34cd70c53 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.Development.json +++ b/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.Development.json @@ -1,8 +1,8 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } } - } } diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.json b/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.json index 10f68b8c8b..23160a4d56 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.json +++ b/src/examples/ReactiveUI.Builder.BlazorServer/appsettings.json @@ -1,9 +1,9 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" } diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/app.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/app.css index 73a69d6f68..d601d9f100 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/app.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/app.css @@ -13,7 +13,7 @@ a, .btn-link { } .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; + box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; } .content { @@ -42,9 +42,9 @@ h1:focus { color: white; } - .blazor-error-boundary::after { - content: "An error has occurred." - } +.blazor-error-boundary::after { + content: "An error has occurred." +} .darker-border-checkbox.form-check-input { border-color: #929292; @@ -57,4 +57,4 @@ h1:focus { .form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { text-align: start; -} \ No newline at end of file +} diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css index 3882a8199b..d8ebeef3d7 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css @@ -10,4076 +10,5042 @@ .container-lg, .container-md, .container-sm { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - width: 100%; - padding-right: calc(var(--bs-gutter-x) * 0.5); - padding-left: calc(var(--bs-gutter-x) * 0.5); - margin-right: auto; - margin-left: auto; + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + width: 100%; + padding-right: calc(var(--bs-gutter-x) * 0.5); + padding-left: calc(var(--bs-gutter-x) * 0.5); + margin-right: auto; + margin-left: auto; } @media (min-width: 576px) { - .container-sm, .container { - max-width: 540px; - } + .container-sm, .container { + max-width: 540px; + } } + @media (min-width: 768px) { - .container-md, .container-sm, .container { - max-width: 720px; - } + .container-md, .container-sm, .container { + max-width: 720px; + } } + @media (min-width: 992px) { - .container-lg, .container-md, .container-sm, .container { - max-width: 960px; - } + .container-lg, .container-md, .container-sm, .container { + max-width: 960px; + } } + @media (min-width: 1200px) { - .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1140px; - } + .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1140px; + } } + @media (min-width: 1400px) { - .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1320px; - } + .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1320px; + } } + :root { - --bs-breakpoint-xs: 0; - --bs-breakpoint-sm: 576px; - --bs-breakpoint-md: 768px; - --bs-breakpoint-lg: 992px; - --bs-breakpoint-xl: 1200px; - --bs-breakpoint-xxl: 1400px; + --bs-breakpoint-xs: 0; + --bs-breakpoint-sm: 576px; + --bs-breakpoint-md: 768px; + --bs-breakpoint-lg: 992px; + --bs-breakpoint-xl: 1200px; + --bs-breakpoint-xxl: 1400px; } .row { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - display: flex; - flex-wrap: wrap; - margin-top: calc(-1 * var(--bs-gutter-y)); - margin-right: calc(-0.5 * var(--bs-gutter-x)); - margin-left: calc(-0.5 * var(--bs-gutter-x)); + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + display: flex; + flex-wrap: wrap; + margin-top: calc(-1 * var(--bs-gutter-y)); + margin-right: calc(-0.5 * var(--bs-gutter-x)); + margin-left: calc(-0.5 * var(--bs-gutter-x)); } + .row > * { - box-sizing: border-box; - flex-shrink: 0; - width: 100%; - max-width: 100%; - padding-right: calc(var(--bs-gutter-x) * 0.5); - padding-left: calc(var(--bs-gutter-x) * 0.5); - margin-top: var(--bs-gutter-y); + box-sizing: border-box; + flex-shrink: 0; + width: 100%; + max-width: 100%; + padding-right: calc(var(--bs-gutter-x) * 0.5); + padding-left: calc(var(--bs-gutter-x) * 0.5); + margin-top: var(--bs-gutter-y); } .col { - flex: 1 0 0%; + flex: 1 0 0%; } .row-cols-auto > * { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .row-cols-1 > * { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .row-cols-2 > * { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .row-cols-3 > * { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .row-cols-4 > * { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .row-cols-5 > * { - flex: 0 0 auto; - width: 20%; + flex: 0 0 auto; + width: 20%; } .row-cols-6 > * { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-auto { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .col-1 { - flex: 0 0 auto; - width: 8.33333333%; + flex: 0 0 auto; + width: 8.33333333%; } .col-2 { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-3 { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .col-4 { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .col-5 { - flex: 0 0 auto; - width: 41.66666667%; + flex: 0 0 auto; + width: 41.66666667%; } .col-6 { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .col-7 { - flex: 0 0 auto; - width: 58.33333333%; + flex: 0 0 auto; + width: 58.33333333%; } .col-8 { - flex: 0 0 auto; - width: 66.66666667%; + flex: 0 0 auto; + width: 66.66666667%; } .col-9 { - flex: 0 0 auto; - width: 75%; + flex: 0 0 auto; + width: 75%; } .col-10 { - flex: 0 0 auto; - width: 83.33333333%; + flex: 0 0 auto; + width: 83.33333333%; } .col-11 { - flex: 0 0 auto; - width: 91.66666667%; + flex: 0 0 auto; + width: 91.66666667%; } .col-12 { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .offset-1 { - margin-left: 8.33333333%; + margin-left: 8.33333333%; } .offset-2 { - margin-left: 16.66666667%; + margin-left: 16.66666667%; } .offset-3 { - margin-left: 25%; + margin-left: 25%; } .offset-4 { - margin-left: 33.33333333%; + margin-left: 33.33333333%; } .offset-5 { - margin-left: 41.66666667%; + margin-left: 41.66666667%; } .offset-6 { - margin-left: 50%; + margin-left: 50%; } .offset-7 { - margin-left: 58.33333333%; + margin-left: 58.33333333%; } .offset-8 { - margin-left: 66.66666667%; + margin-left: 66.66666667%; } .offset-9 { - margin-left: 75%; + margin-left: 75%; } .offset-10 { - margin-left: 83.33333333%; + margin-left: 83.33333333%; } .offset-11 { - margin-left: 91.66666667%; + margin-left: 91.66666667%; } .g-0, .gx-0 { - --bs-gutter-x: 0; + --bs-gutter-x: 0; } .g-0, .gy-0 { - --bs-gutter-y: 0; + --bs-gutter-y: 0; } .g-1, .gx-1 { - --bs-gutter-x: 0.25rem; + --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { - --bs-gutter-y: 0.25rem; + --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { - --bs-gutter-x: 0.5rem; + --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { - --bs-gutter-y: 0.5rem; + --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { - --bs-gutter-x: 1rem; + --bs-gutter-x: 1rem; } .g-3, .gy-3 { - --bs-gutter-y: 1rem; + --bs-gutter-y: 1rem; } .g-4, .gx-4 { - --bs-gutter-x: 1.5rem; + --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { - --bs-gutter-y: 1.5rem; + --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { - --bs-gutter-x: 3rem; + --bs-gutter-x: 3rem; } .g-5, .gy-5 { - --bs-gutter-y: 3rem; + --bs-gutter-y: 3rem; } @media (min-width: 576px) { - .col-sm { - flex: 1 0 0%; - } - .row-cols-sm-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-sm-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-sm-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-sm-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-sm-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-sm-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-sm-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-auto { - flex: 0 0 auto; - width: auto; - } - .col-sm-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-sm-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-3 { - flex: 0 0 auto; - width: 25%; - } - .col-sm-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-sm-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-sm-6 { - flex: 0 0 auto; - width: 50%; - } - .col-sm-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-sm-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-sm-9 { - flex: 0 0 auto; - width: 75%; - } - .col-sm-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-sm-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-sm-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-sm-0 { - margin-left: 0; - } - .offset-sm-1 { - margin-left: 8.33333333%; - } - .offset-sm-2 { - margin-left: 16.66666667%; - } - .offset-sm-3 { - margin-left: 25%; - } - .offset-sm-4 { - margin-left: 33.33333333%; - } - .offset-sm-5 { - margin-left: 41.66666667%; - } - .offset-sm-6 { - margin-left: 50%; - } - .offset-sm-7 { - margin-left: 58.33333333%; - } - .offset-sm-8 { - margin-left: 66.66666667%; - } - .offset-sm-9 { - margin-left: 75%; - } - .offset-sm-10 { - margin-left: 83.33333333%; - } - .offset-sm-11 { - margin-left: 91.66666667%; - } - .g-sm-0, - .gx-sm-0 { - --bs-gutter-x: 0; - } - .g-sm-0, - .gy-sm-0 { - --bs-gutter-y: 0; - } - .g-sm-1, - .gx-sm-1 { - --bs-gutter-x: 0.25rem; - } - .g-sm-1, - .gy-sm-1 { - --bs-gutter-y: 0.25rem; - } - .g-sm-2, - .gx-sm-2 { - --bs-gutter-x: 0.5rem; - } - .g-sm-2, - .gy-sm-2 { - --bs-gutter-y: 0.5rem; - } - .g-sm-3, - .gx-sm-3 { - --bs-gutter-x: 1rem; - } - .g-sm-3, - .gy-sm-3 { - --bs-gutter-y: 1rem; - } - .g-sm-4, - .gx-sm-4 { - --bs-gutter-x: 1.5rem; - } - .g-sm-4, - .gy-sm-4 { - --bs-gutter-y: 1.5rem; - } - .g-sm-5, - .gx-sm-5 { - --bs-gutter-x: 3rem; - } - .g-sm-5, - .gy-sm-5 { - --bs-gutter-y: 3rem; - } + .col-sm { + flex: 1 0 0%; + } + + .row-cols-sm-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-sm-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-sm-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-sm-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-sm-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-sm-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-sm-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + } + + .col-sm-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-sm-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-sm-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-sm-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-sm-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-sm-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-sm-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-sm-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-sm-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-sm-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-sm-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-sm-0 { + margin-left: 0; + } + + .offset-sm-1 { + margin-left: 8.33333333%; + } + + .offset-sm-2 { + margin-left: 16.66666667%; + } + + .offset-sm-3 { + margin-left: 25%; + } + + .offset-sm-4 { + margin-left: 33.33333333%; + } + + .offset-sm-5 { + margin-left: 41.66666667%; + } + + .offset-sm-6 { + margin-left: 50%; + } + + .offset-sm-7 { + margin-left: 58.33333333%; + } + + .offset-sm-8 { + margin-left: 66.66666667%; + } + + .offset-sm-9 { + margin-left: 75%; + } + + .offset-sm-10 { + margin-left: 83.33333333%; + } + + .offset-sm-11 { + margin-left: 91.66666667%; + } + + .g-sm-0, + .gx-sm-0 { + --bs-gutter-x: 0; + } + + .g-sm-0, + .gy-sm-0 { + --bs-gutter-y: 0; + } + + .g-sm-1, + .gx-sm-1 { + --bs-gutter-x: 0.25rem; + } + + .g-sm-1, + .gy-sm-1 { + --bs-gutter-y: 0.25rem; + } + + .g-sm-2, + .gx-sm-2 { + --bs-gutter-x: 0.5rem; + } + + .g-sm-2, + .gy-sm-2 { + --bs-gutter-y: 0.5rem; + } + + .g-sm-3, + .gx-sm-3 { + --bs-gutter-x: 1rem; + } + + .g-sm-3, + .gy-sm-3 { + --bs-gutter-y: 1rem; + } + + .g-sm-4, + .gx-sm-4 { + --bs-gutter-x: 1.5rem; + } + + .g-sm-4, + .gy-sm-4 { + --bs-gutter-y: 1.5rem; + } + + .g-sm-5, + .gx-sm-5 { + --bs-gutter-x: 3rem; + } + + .g-sm-5, + .gy-sm-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 768px) { - .col-md { - flex: 1 0 0%; - } - .row-cols-md-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-md-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-md-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-md-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-md-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-md-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-md-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-auto { - flex: 0 0 auto; - width: auto; - } - .col-md-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-md-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-3 { - flex: 0 0 auto; - width: 25%; - } - .col-md-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-md-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-md-6 { - flex: 0 0 auto; - width: 50%; - } - .col-md-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-md-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-md-9 { - flex: 0 0 auto; - width: 75%; - } - .col-md-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-md-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-md-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-md-0 { - margin-left: 0; - } - .offset-md-1 { - margin-left: 8.33333333%; - } - .offset-md-2 { - margin-left: 16.66666667%; - } - .offset-md-3 { - margin-left: 25%; - } - .offset-md-4 { - margin-left: 33.33333333%; - } - .offset-md-5 { - margin-left: 41.66666667%; - } - .offset-md-6 { - margin-left: 50%; - } - .offset-md-7 { - margin-left: 58.33333333%; - } - .offset-md-8 { - margin-left: 66.66666667%; - } - .offset-md-9 { - margin-left: 75%; - } - .offset-md-10 { - margin-left: 83.33333333%; - } - .offset-md-11 { - margin-left: 91.66666667%; - } - .g-md-0, - .gx-md-0 { - --bs-gutter-x: 0; - } - .g-md-0, - .gy-md-0 { - --bs-gutter-y: 0; - } - .g-md-1, - .gx-md-1 { - --bs-gutter-x: 0.25rem; - } - .g-md-1, - .gy-md-1 { - --bs-gutter-y: 0.25rem; - } - .g-md-2, - .gx-md-2 { - --bs-gutter-x: 0.5rem; - } - .g-md-2, - .gy-md-2 { - --bs-gutter-y: 0.5rem; - } - .g-md-3, - .gx-md-3 { - --bs-gutter-x: 1rem; - } - .g-md-3, - .gy-md-3 { - --bs-gutter-y: 1rem; - } - .g-md-4, - .gx-md-4 { - --bs-gutter-x: 1.5rem; - } - .g-md-4, - .gy-md-4 { - --bs-gutter-y: 1.5rem; - } - .g-md-5, - .gx-md-5 { - --bs-gutter-x: 3rem; - } - .g-md-5, - .gy-md-5 { - --bs-gutter-y: 3rem; - } + .col-md { + flex: 1 0 0%; + } + + .row-cols-md-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-md-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-md-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-md-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-md-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-md-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-md-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + } + + .col-md-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-md-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-md-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-md-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-md-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-md-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-md-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-md-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-md-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-md-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-md-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-md-0 { + margin-left: 0; + } + + .offset-md-1 { + margin-left: 8.33333333%; + } + + .offset-md-2 { + margin-left: 16.66666667%; + } + + .offset-md-3 { + margin-left: 25%; + } + + .offset-md-4 { + margin-left: 33.33333333%; + } + + .offset-md-5 { + margin-left: 41.66666667%; + } + + .offset-md-6 { + margin-left: 50%; + } + + .offset-md-7 { + margin-left: 58.33333333%; + } + + .offset-md-8 { + margin-left: 66.66666667%; + } + + .offset-md-9 { + margin-left: 75%; + } + + .offset-md-10 { + margin-left: 83.33333333%; + } + + .offset-md-11 { + margin-left: 91.66666667%; + } + + .g-md-0, + .gx-md-0 { + --bs-gutter-x: 0; + } + + .g-md-0, + .gy-md-0 { + --bs-gutter-y: 0; + } + + .g-md-1, + .gx-md-1 { + --bs-gutter-x: 0.25rem; + } + + .g-md-1, + .gy-md-1 { + --bs-gutter-y: 0.25rem; + } + + .g-md-2, + .gx-md-2 { + --bs-gutter-x: 0.5rem; + } + + .g-md-2, + .gy-md-2 { + --bs-gutter-y: 0.5rem; + } + + .g-md-3, + .gx-md-3 { + --bs-gutter-x: 1rem; + } + + .g-md-3, + .gy-md-3 { + --bs-gutter-y: 1rem; + } + + .g-md-4, + .gx-md-4 { + --bs-gutter-x: 1.5rem; + } + + .g-md-4, + .gy-md-4 { + --bs-gutter-y: 1.5rem; + } + + .g-md-5, + .gx-md-5 { + --bs-gutter-x: 3rem; + } + + .g-md-5, + .gy-md-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 992px) { - .col-lg { - flex: 1 0 0%; - } - .row-cols-lg-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-lg-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-lg-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-lg-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-lg-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-lg-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-lg-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-auto { - flex: 0 0 auto; - width: auto; - } - .col-lg-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-lg-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-3 { - flex: 0 0 auto; - width: 25%; - } - .col-lg-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-lg-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-lg-6 { - flex: 0 0 auto; - width: 50%; - } - .col-lg-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-lg-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-lg-9 { - flex: 0 0 auto; - width: 75%; - } - .col-lg-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-lg-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-lg-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-lg-0 { - margin-left: 0; - } - .offset-lg-1 { - margin-left: 8.33333333%; - } - .offset-lg-2 { - margin-left: 16.66666667%; - } - .offset-lg-3 { - margin-left: 25%; - } - .offset-lg-4 { - margin-left: 33.33333333%; - } - .offset-lg-5 { - margin-left: 41.66666667%; - } - .offset-lg-6 { - margin-left: 50%; - } - .offset-lg-7 { - margin-left: 58.33333333%; - } - .offset-lg-8 { - margin-left: 66.66666667%; - } - .offset-lg-9 { - margin-left: 75%; - } - .offset-lg-10 { - margin-left: 83.33333333%; - } - .offset-lg-11 { - margin-left: 91.66666667%; - } - .g-lg-0, - .gx-lg-0 { - --bs-gutter-x: 0; - } - .g-lg-0, - .gy-lg-0 { - --bs-gutter-y: 0; - } - .g-lg-1, - .gx-lg-1 { - --bs-gutter-x: 0.25rem; - } - .g-lg-1, - .gy-lg-1 { - --bs-gutter-y: 0.25rem; - } - .g-lg-2, - .gx-lg-2 { - --bs-gutter-x: 0.5rem; - } - .g-lg-2, - .gy-lg-2 { - --bs-gutter-y: 0.5rem; - } - .g-lg-3, - .gx-lg-3 { - --bs-gutter-x: 1rem; - } - .g-lg-3, - .gy-lg-3 { - --bs-gutter-y: 1rem; - } - .g-lg-4, - .gx-lg-4 { - --bs-gutter-x: 1.5rem; - } - .g-lg-4, - .gy-lg-4 { - --bs-gutter-y: 1.5rem; - } - .g-lg-5, - .gx-lg-5 { - --bs-gutter-x: 3rem; - } - .g-lg-5, - .gy-lg-5 { - --bs-gutter-y: 3rem; - } + .col-lg { + flex: 1 0 0%; + } + + .row-cols-lg-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-lg-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-lg-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-lg-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-lg-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-lg-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-lg-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + } + + .col-lg-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-lg-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-lg-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-lg-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-lg-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-lg-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-lg-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-lg-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-lg-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-lg-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-lg-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-lg-0 { + margin-left: 0; + } + + .offset-lg-1 { + margin-left: 8.33333333%; + } + + .offset-lg-2 { + margin-left: 16.66666667%; + } + + .offset-lg-3 { + margin-left: 25%; + } + + .offset-lg-4 { + margin-left: 33.33333333%; + } + + .offset-lg-5 { + margin-left: 41.66666667%; + } + + .offset-lg-6 { + margin-left: 50%; + } + + .offset-lg-7 { + margin-left: 58.33333333%; + } + + .offset-lg-8 { + margin-left: 66.66666667%; + } + + .offset-lg-9 { + margin-left: 75%; + } + + .offset-lg-10 { + margin-left: 83.33333333%; + } + + .offset-lg-11 { + margin-left: 91.66666667%; + } + + .g-lg-0, + .gx-lg-0 { + --bs-gutter-x: 0; + } + + .g-lg-0, + .gy-lg-0 { + --bs-gutter-y: 0; + } + + .g-lg-1, + .gx-lg-1 { + --bs-gutter-x: 0.25rem; + } + + .g-lg-1, + .gy-lg-1 { + --bs-gutter-y: 0.25rem; + } + + .g-lg-2, + .gx-lg-2 { + --bs-gutter-x: 0.5rem; + } + + .g-lg-2, + .gy-lg-2 { + --bs-gutter-y: 0.5rem; + } + + .g-lg-3, + .gx-lg-3 { + --bs-gutter-x: 1rem; + } + + .g-lg-3, + .gy-lg-3 { + --bs-gutter-y: 1rem; + } + + .g-lg-4, + .gx-lg-4 { + --bs-gutter-x: 1.5rem; + } + + .g-lg-4, + .gy-lg-4 { + --bs-gutter-y: 1.5rem; + } + + .g-lg-5, + .gx-lg-5 { + --bs-gutter-x: 3rem; + } + + .g-lg-5, + .gy-lg-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 1200px) { - .col-xl { - flex: 1 0 0%; - } - .row-cols-xl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xl-0 { - margin-left: 0; - } - .offset-xl-1 { - margin-left: 8.33333333%; - } - .offset-xl-2 { - margin-left: 16.66666667%; - } - .offset-xl-3 { - margin-left: 25%; - } - .offset-xl-4 { - margin-left: 33.33333333%; - } - .offset-xl-5 { - margin-left: 41.66666667%; - } - .offset-xl-6 { - margin-left: 50%; - } - .offset-xl-7 { - margin-left: 58.33333333%; - } - .offset-xl-8 { - margin-left: 66.66666667%; - } - .offset-xl-9 { - margin-left: 75%; - } - .offset-xl-10 { - margin-left: 83.33333333%; - } - .offset-xl-11 { - margin-left: 91.66666667%; - } - .g-xl-0, - .gx-xl-0 { - --bs-gutter-x: 0; - } - .g-xl-0, - .gy-xl-0 { - --bs-gutter-y: 0; - } - .g-xl-1, - .gx-xl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xl-1, - .gy-xl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xl-2, - .gx-xl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xl-2, - .gy-xl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xl-3, - .gx-xl-3 { - --bs-gutter-x: 1rem; - } - .g-xl-3, - .gy-xl-3 { - --bs-gutter-y: 1rem; - } - .g-xl-4, - .gx-xl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xl-4, - .gy-xl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xl-5, - .gx-xl-5 { - --bs-gutter-x: 3rem; - } - .g-xl-5, - .gy-xl-5 { - --bs-gutter-y: 3rem; - } + .col-xl { + flex: 1 0 0%; + } + + .row-cols-xl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xl-0 { + margin-left: 0; + } + + .offset-xl-1 { + margin-left: 8.33333333%; + } + + .offset-xl-2 { + margin-left: 16.66666667%; + } + + .offset-xl-3 { + margin-left: 25%; + } + + .offset-xl-4 { + margin-left: 33.33333333%; + } + + .offset-xl-5 { + margin-left: 41.66666667%; + } + + .offset-xl-6 { + margin-left: 50%; + } + + .offset-xl-7 { + margin-left: 58.33333333%; + } + + .offset-xl-8 { + margin-left: 66.66666667%; + } + + .offset-xl-9 { + margin-left: 75%; + } + + .offset-xl-10 { + margin-left: 83.33333333%; + } + + .offset-xl-11 { + margin-left: 91.66666667%; + } + + .g-xl-0, + .gx-xl-0 { + --bs-gutter-x: 0; + } + + .g-xl-0, + .gy-xl-0 { + --bs-gutter-y: 0; + } + + .g-xl-1, + .gx-xl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xl-1, + .gy-xl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xl-2, + .gx-xl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xl-2, + .gy-xl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xl-3, + .gx-xl-3 { + --bs-gutter-x: 1rem; + } + + .g-xl-3, + .gy-xl-3 { + --bs-gutter-y: 1rem; + } + + .g-xl-4, + .gx-xl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xl-4, + .gy-xl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xl-5, + .gx-xl-5 { + --bs-gutter-x: 3rem; + } + + .g-xl-5, + .gy-xl-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 1400px) { - .col-xxl { - flex: 1 0 0%; - } - .row-cols-xxl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xxl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xxl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xxl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xxl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xxl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xxl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xxl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xxl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xxl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xxl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xxl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xxl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xxl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xxl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xxl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xxl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xxl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xxl-0 { - margin-left: 0; - } - .offset-xxl-1 { - margin-left: 8.33333333%; - } - .offset-xxl-2 { - margin-left: 16.66666667%; - } - .offset-xxl-3 { - margin-left: 25%; - } - .offset-xxl-4 { - margin-left: 33.33333333%; - } - .offset-xxl-5 { - margin-left: 41.66666667%; - } - .offset-xxl-6 { - margin-left: 50%; - } - .offset-xxl-7 { - margin-left: 58.33333333%; - } - .offset-xxl-8 { - margin-left: 66.66666667%; - } - .offset-xxl-9 { - margin-left: 75%; - } - .offset-xxl-10 { - margin-left: 83.33333333%; - } - .offset-xxl-11 { - margin-left: 91.66666667%; - } - .g-xxl-0, - .gx-xxl-0 { - --bs-gutter-x: 0; - } - .g-xxl-0, - .gy-xxl-0 { - --bs-gutter-y: 0; - } - .g-xxl-1, - .gx-xxl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xxl-1, - .gy-xxl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xxl-2, - .gx-xxl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xxl-2, - .gy-xxl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xxl-3, - .gx-xxl-3 { - --bs-gutter-x: 1rem; - } - .g-xxl-3, - .gy-xxl-3 { - --bs-gutter-y: 1rem; - } - .g-xxl-4, - .gx-xxl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xxl-4, - .gy-xxl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xxl-5, - .gx-xxl-5 { - --bs-gutter-x: 3rem; - } - .g-xxl-5, - .gy-xxl-5 { - --bs-gutter-y: 3rem; - } + .col-xxl { + flex: 1 0 0%; + } + + .row-cols-xxl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xxl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xxl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xxl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xxl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xxl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xxl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xxl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xxl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xxl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xxl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xxl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xxl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xxl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xxl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xxl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xxl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xxl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xxl-0 { + margin-left: 0; + } + + .offset-xxl-1 { + margin-left: 8.33333333%; + } + + .offset-xxl-2 { + margin-left: 16.66666667%; + } + + .offset-xxl-3 { + margin-left: 25%; + } + + .offset-xxl-4 { + margin-left: 33.33333333%; + } + + .offset-xxl-5 { + margin-left: 41.66666667%; + } + + .offset-xxl-6 { + margin-left: 50%; + } + + .offset-xxl-7 { + margin-left: 58.33333333%; + } + + .offset-xxl-8 { + margin-left: 66.66666667%; + } + + .offset-xxl-9 { + margin-left: 75%; + } + + .offset-xxl-10 { + margin-left: 83.33333333%; + } + + .offset-xxl-11 { + margin-left: 91.66666667%; + } + + .g-xxl-0, + .gx-xxl-0 { + --bs-gutter-x: 0; + } + + .g-xxl-0, + .gy-xxl-0 { + --bs-gutter-y: 0; + } + + .g-xxl-1, + .gx-xxl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xxl-1, + .gy-xxl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xxl-2, + .gx-xxl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xxl-2, + .gy-xxl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xxl-3, + .gx-xxl-3 { + --bs-gutter-x: 1rem; + } + + .g-xxl-3, + .gy-xxl-3 { + --bs-gutter-y: 1rem; + } + + .g-xxl-4, + .gx-xxl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xxl-4, + .gy-xxl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xxl-5, + .gx-xxl-5 { + --bs-gutter-x: 3rem; + } + + .g-xxl-5, + .gy-xxl-5 { + --bs-gutter-y: 3rem; + } } + .d-inline { - display: inline !important; + display: inline !important; } .d-inline-block { - display: inline-block !important; + display: inline-block !important; } .d-block { - display: block !important; + display: block !important; } .d-grid { - display: grid !important; + display: grid !important; } .d-inline-grid { - display: inline-grid !important; + display: inline-grid !important; } .d-table { - display: table !important; + display: table !important; } .d-table-row { - display: table-row !important; + display: table-row !important; } .d-table-cell { - display: table-cell !important; + display: table-cell !important; } .d-flex { - display: flex !important; + display: flex !important; } .d-inline-flex { - display: inline-flex !important; + display: inline-flex !important; } .d-none { - display: none !important; + display: none !important; } .flex-fill { - flex: 1 1 auto !important; + flex: 1 1 auto !important; } .flex-row { - flex-direction: row !important; + flex-direction: row !important; } .flex-column { - flex-direction: column !important; + flex-direction: column !important; } .flex-row-reverse { - flex-direction: row-reverse !important; + flex-direction: row-reverse !important; } .flex-column-reverse { - flex-direction: column-reverse !important; + flex-direction: column-reverse !important; } .flex-grow-0 { - flex-grow: 0 !important; + flex-grow: 0 !important; } .flex-grow-1 { - flex-grow: 1 !important; + flex-grow: 1 !important; } .flex-shrink-0 { - flex-shrink: 0 !important; + flex-shrink: 0 !important; } .flex-shrink-1 { - flex-shrink: 1 !important; + flex-shrink: 1 !important; } .flex-wrap { - flex-wrap: wrap !important; + flex-wrap: wrap !important; } .flex-nowrap { - flex-wrap: nowrap !important; + flex-wrap: nowrap !important; } .flex-wrap-reverse { - flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; } .justify-content-start { - justify-content: flex-start !important; + justify-content: flex-start !important; } .justify-content-end { - justify-content: flex-end !important; + justify-content: flex-end !important; } .justify-content-center { - justify-content: center !important; + justify-content: center !important; } .justify-content-between { - justify-content: space-between !important; + justify-content: space-between !important; } .justify-content-around { - justify-content: space-around !important; + justify-content: space-around !important; } .justify-content-evenly { - justify-content: space-evenly !important; + justify-content: space-evenly !important; } .align-items-start { - align-items: flex-start !important; + align-items: flex-start !important; } .align-items-end { - align-items: flex-end !important; + align-items: flex-end !important; } .align-items-center { - align-items: center !important; + align-items: center !important; } .align-items-baseline { - align-items: baseline !important; + align-items: baseline !important; } .align-items-stretch { - align-items: stretch !important; + align-items: stretch !important; } .align-content-start { - align-content: flex-start !important; + align-content: flex-start !important; } .align-content-end { - align-content: flex-end !important; + align-content: flex-end !important; } .align-content-center { - align-content: center !important; + align-content: center !important; } .align-content-between { - align-content: space-between !important; + align-content: space-between !important; } .align-content-around { - align-content: space-around !important; + align-content: space-around !important; } .align-content-stretch { - align-content: stretch !important; + align-content: stretch !important; } .align-self-auto { - align-self: auto !important; + align-self: auto !important; } .align-self-start { - align-self: flex-start !important; + align-self: flex-start !important; } .align-self-end { - align-self: flex-end !important; + align-self: flex-end !important; } .align-self-center { - align-self: center !important; + align-self: center !important; } .align-self-baseline { - align-self: baseline !important; + align-self: baseline !important; } .align-self-stretch { - align-self: stretch !important; + align-self: stretch !important; } .order-first { - order: -1 !important; + order: -1 !important; } .order-0 { - order: 0 !important; + order: 0 !important; } .order-1 { - order: 1 !important; + order: 1 !important; } .order-2 { - order: 2 !important; + order: 2 !important; } .order-3 { - order: 3 !important; + order: 3 !important; } .order-4 { - order: 4 !important; + order: 4 !important; } .order-5 { - order: 5 !important; + order: 5 !important; } .order-last { - order: 6 !important; + order: 6 !important; } .m-0 { - margin: 0 !important; + margin: 0 !important; } .m-1 { - margin: 0.25rem !important; + margin: 0.25rem !important; } .m-2 { - margin: 0.5rem !important; + margin: 0.5rem !important; } .m-3 { - margin: 1rem !important; + margin: 1rem !important; } .m-4 { - margin: 1.5rem !important; + margin: 1.5rem !important; } .m-5 { - margin: 3rem !important; + margin: 3rem !important; } .m-auto { - margin: auto !important; + margin: auto !important; } .mx-0 { - margin-right: 0 !important; - margin-left: 0 !important; + margin-right: 0 !important; + margin-left: 0 !important; } .mx-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } .mx-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } .mx-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; + margin-right: 1rem !important; + margin-left: 1rem !important; } .mx-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } .mx-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; + margin-right: 3rem !important; + margin-left: 3rem !important; } .mx-auto { - margin-right: auto !important; - margin-left: auto !important; + margin-right: auto !important; + margin-left: auto !important; } .my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; } .my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } .my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } .my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; + margin-top: 1rem !important; + margin-bottom: 1rem !important; } .my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } .my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; + margin-top: 3rem !important; + margin-bottom: 3rem !important; } .my-auto { - margin-top: auto !important; - margin-bottom: auto !important; + margin-top: auto !important; + margin-bottom: auto !important; } .mt-0 { - margin-top: 0 !important; + margin-top: 0 !important; } .mt-1 { - margin-top: 0.25rem !important; + margin-top: 0.25rem !important; } .mt-2 { - margin-top: 0.5rem !important; + margin-top: 0.5rem !important; } .mt-3 { - margin-top: 1rem !important; + margin-top: 1rem !important; } .mt-4 { - margin-top: 1.5rem !important; + margin-top: 1.5rem !important; } .mt-5 { - margin-top: 3rem !important; + margin-top: 3rem !important; } .mt-auto { - margin-top: auto !important; + margin-top: auto !important; } .me-0 { - margin-right: 0 !important; + margin-right: 0 !important; } .me-1 { - margin-right: 0.25rem !important; + margin-right: 0.25rem !important; } .me-2 { - margin-right: 0.5rem !important; + margin-right: 0.5rem !important; } .me-3 { - margin-right: 1rem !important; + margin-right: 1rem !important; } .me-4 { - margin-right: 1.5rem !important; + margin-right: 1.5rem !important; } .me-5 { - margin-right: 3rem !important; + margin-right: 3rem !important; } .me-auto { - margin-right: auto !important; + margin-right: auto !important; } .mb-0 { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .mb-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 0.25rem !important; } .mb-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 0.5rem !important; } .mb-3 { - margin-bottom: 1rem !important; + margin-bottom: 1rem !important; } .mb-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 1.5rem !important; } .mb-5 { - margin-bottom: 3rem !important; + margin-bottom: 3rem !important; } .mb-auto { - margin-bottom: auto !important; + margin-bottom: auto !important; } .ms-0 { - margin-left: 0 !important; + margin-left: 0 !important; } .ms-1 { - margin-left: 0.25rem !important; + margin-left: 0.25rem !important; } .ms-2 { - margin-left: 0.5rem !important; + margin-left: 0.5rem !important; } .ms-3 { - margin-left: 1rem !important; + margin-left: 1rem !important; } .ms-4 { - margin-left: 1.5rem !important; + margin-left: 1.5rem !important; } .ms-5 { - margin-left: 3rem !important; + margin-left: 3rem !important; } .ms-auto { - margin-left: auto !important; + margin-left: auto !important; } .p-0 { - padding: 0 !important; + padding: 0 !important; } .p-1 { - padding: 0.25rem !important; + padding: 0.25rem !important; } .p-2 { - padding: 0.5rem !important; + padding: 0.5rem !important; } .p-3 { - padding: 1rem !important; + padding: 1rem !important; } .p-4 { - padding: 1.5rem !important; + padding: 1.5rem !important; } .p-5 { - padding: 3rem !important; + padding: 3rem !important; } .px-0 { - padding-right: 0 !important; - padding-left: 0 !important; + padding-right: 0 !important; + padding-left: 0 !important; } .px-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } .px-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } .px-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; + padding-right: 1rem !important; + padding-left: 1rem !important; } .px-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } .px-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; + padding-right: 3rem !important; + padding-left: 3rem !important; } .py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; } .py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } .py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } .py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; + padding-top: 1rem !important; + padding-bottom: 1rem !important; } .py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } .py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; + padding-top: 3rem !important; + padding-bottom: 3rem !important; } .pt-0 { - padding-top: 0 !important; + padding-top: 0 !important; } .pt-1 { - padding-top: 0.25rem !important; + padding-top: 0.25rem !important; } .pt-2 { - padding-top: 0.5rem !important; + padding-top: 0.5rem !important; } .pt-3 { - padding-top: 1rem !important; + padding-top: 1rem !important; } .pt-4 { - padding-top: 1.5rem !important; + padding-top: 1.5rem !important; } .pt-5 { - padding-top: 3rem !important; + padding-top: 3rem !important; } .pe-0 { - padding-right: 0 !important; + padding-right: 0 !important; } .pe-1 { - padding-right: 0.25rem !important; + padding-right: 0.25rem !important; } .pe-2 { - padding-right: 0.5rem !important; + padding-right: 0.5rem !important; } .pe-3 { - padding-right: 1rem !important; + padding-right: 1rem !important; } .pe-4 { - padding-right: 1.5rem !important; + padding-right: 1.5rem !important; } .pe-5 { - padding-right: 3rem !important; + padding-right: 3rem !important; } .pb-0 { - padding-bottom: 0 !important; + padding-bottom: 0 !important; } .pb-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 0.25rem !important; } .pb-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 0.5rem !important; } .pb-3 { - padding-bottom: 1rem !important; + padding-bottom: 1rem !important; } .pb-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 1.5rem !important; } .pb-5 { - padding-bottom: 3rem !important; + padding-bottom: 3rem !important; } .ps-0 { - padding-left: 0 !important; + padding-left: 0 !important; } .ps-1 { - padding-left: 0.25rem !important; + padding-left: 0.25rem !important; } .ps-2 { - padding-left: 0.5rem !important; + padding-left: 0.5rem !important; } .ps-3 { - padding-left: 1rem !important; + padding-left: 1rem !important; } .ps-4 { - padding-left: 1.5rem !important; + padding-left: 1.5rem !important; } .ps-5 { - padding-left: 3rem !important; + padding-left: 3rem !important; } @media (min-width: 576px) { - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-sm-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-sm-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-sm-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-sm-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-sm-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-sm-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-right: 0 !important; - } - .me-sm-1 { - margin-right: 0.25rem !important; - } - .me-sm-2 { - margin-right: 0.5rem !important; - } - .me-sm-3 { - margin-right: 1rem !important; - } - .me-sm-4 { - margin-right: 1.5rem !important; - } - .me-sm-5 { - margin-right: 3rem !important; - } - .me-sm-auto { - margin-right: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-left: 0 !important; - } - .ms-sm-1 { - margin-left: 0.25rem !important; - } - .ms-sm-2 { - margin-left: 0.5rem !important; - } - .ms-sm-3 { - margin-left: 1rem !important; - } - .ms-sm-4 { - margin-left: 1.5rem !important; - } - .ms-sm-5 { - margin-left: 3rem !important; - } - .ms-sm-auto { - margin-left: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-sm-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-sm-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-sm-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-sm-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-sm-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-right: 0 !important; - } - .pe-sm-1 { - padding-right: 0.25rem !important; - } - .pe-sm-2 { - padding-right: 0.5rem !important; - } - .pe-sm-3 { - padding-right: 1rem !important; - } - .pe-sm-4 { - padding-right: 1.5rem !important; - } - .pe-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-left: 0 !important; - } - .ps-sm-1 { - padding-left: 0.25rem !important; - } - .ps-sm-2 { - padding-left: 0.5rem !important; - } - .ps-sm-3 { - padding-left: 1rem !important; - } - .ps-sm-4 { - padding-left: 1.5rem !important; - } - .ps-sm-5 { - padding-left: 3rem !important; - } -} -@media (min-width: 768px) { - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-md-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-md-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-md-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-md-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-md-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-md-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-right: 0 !important; - } - .me-md-1 { - margin-right: 0.25rem !important; - } - .me-md-2 { - margin-right: 0.5rem !important; - } - .me-md-3 { - margin-right: 1rem !important; - } - .me-md-4 { - margin-right: 1.5rem !important; - } - .me-md-5 { - margin-right: 3rem !important; - } - .me-md-auto { - margin-right: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-left: 0 !important; - } - .ms-md-1 { - margin-left: 0.25rem !important; - } - .ms-md-2 { - margin-left: 0.5rem !important; - } - .ms-md-3 { - margin-left: 1rem !important; - } - .ms-md-4 { - margin-left: 1.5rem !important; - } - .ms-md-5 { - margin-left: 3rem !important; - } - .ms-md-auto { - margin-left: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-md-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-md-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-md-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-md-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-md-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-right: 0 !important; - } - .pe-md-1 { - padding-right: 0.25rem !important; - } - .pe-md-2 { - padding-right: 0.5rem !important; - } - .pe-md-3 { - padding-right: 1rem !important; - } - .pe-md-4 { - padding-right: 1.5rem !important; - } - .pe-md-5 { - padding-right: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-left: 0 !important; - } - .ps-md-1 { - padding-left: 0.25rem !important; - } - .ps-md-2 { - padding-left: 0.5rem !important; - } - .ps-md-3 { - padding-left: 1rem !important; - } - .ps-md-4 { - padding-left: 1.5rem !important; - } - .ps-md-5 { - padding-left: 3rem !important; - } + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-grid { + display: grid !important; + } + + .d-sm-inline-grid { + display: inline-grid !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: flex !important; + } + + .d-sm-inline-flex { + display: inline-flex !important; + } + + .d-sm-none { + display: none !important; + } + + .flex-sm-fill { + flex: 1 1 auto !important; + } + + .flex-sm-row { + flex-direction: row !important; + } + + .flex-sm-column { + flex-direction: column !important; + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-sm-wrap { + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-sm-start { + justify-content: flex-start !important; + } + + .justify-content-sm-end { + justify-content: flex-end !important; + } + + .justify-content-sm-center { + justify-content: center !important; + } + + .justify-content-sm-between { + justify-content: space-between !important; + } + + .justify-content-sm-around { + justify-content: space-around !important; + } + + .justify-content-sm-evenly { + justify-content: space-evenly !important; + } + + .align-items-sm-start { + align-items: flex-start !important; + } + + .align-items-sm-end { + align-items: flex-end !important; + } + + .align-items-sm-center { + align-items: center !important; + } + + .align-items-sm-baseline { + align-items: baseline !important; + } + + .align-items-sm-stretch { + align-items: stretch !important; + } + + .align-content-sm-start { + align-content: flex-start !important; + } + + .align-content-sm-end { + align-content: flex-end !important; + } + + .align-content-sm-center { + align-content: center !important; + } + + .align-content-sm-between { + align-content: space-between !important; + } + + .align-content-sm-around { + align-content: space-around !important; + } + + .align-content-sm-stretch { + align-content: stretch !important; + } + + .align-self-sm-auto { + align-self: auto !important; + } + + .align-self-sm-start { + align-self: flex-start !important; + } + + .align-self-sm-end { + align-self: flex-end !important; + } + + .align-self-sm-center { + align-self: center !important; + } + + .align-self-sm-baseline { + align-self: baseline !important; + } + + .align-self-sm-stretch { + align-self: stretch !important; + } + + .order-sm-first { + order: -1 !important; + } + + .order-sm-0 { + order: 0 !important; + } + + .order-sm-1 { + order: 1 !important; + } + + .order-sm-2 { + order: 2 !important; + } + + .order-sm-3 { + order: 3 !important; + } + + .order-sm-4 { + order: 4 !important; + } + + .order-sm-5 { + order: 5 !important; + } + + .order-sm-last { + order: 6 !important; + } + + .m-sm-0 { + margin: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mx-sm-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-sm-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-sm-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-sm-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-sm-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-sm-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-sm-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-sm-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-sm-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-sm-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-sm-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-sm-0 { + margin-top: 0 !important; + } + + .mt-sm-1 { + margin-top: 0.25rem !important; + } + + .mt-sm-2 { + margin-top: 0.5rem !important; + } + + .mt-sm-3 { + margin-top: 1rem !important; + } + + .mt-sm-4 { + margin-top: 1.5rem !important; + } + + .mt-sm-5 { + margin-top: 3rem !important; + } + + .mt-sm-auto { + margin-top: auto !important; + } + + .me-sm-0 { + margin-right: 0 !important; + } + + .me-sm-1 { + margin-right: 0.25rem !important; + } + + .me-sm-2 { + margin-right: 0.5rem !important; + } + + .me-sm-3 { + margin-right: 1rem !important; + } + + .me-sm-4 { + margin-right: 1.5rem !important; + } + + .me-sm-5 { + margin-right: 3rem !important; + } + + .me-sm-auto { + margin-right: auto !important; + } + + .mb-sm-0 { + margin-bottom: 0 !important; + } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; + } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; + } + + .mb-sm-3 { + margin-bottom: 1rem !important; + } + + .mb-sm-4 { + margin-bottom: 1.5rem !important; + } + + .mb-sm-5 { + margin-bottom: 3rem !important; + } + + .mb-sm-auto { + margin-bottom: auto !important; + } + + .ms-sm-0 { + margin-left: 0 !important; + } + + .ms-sm-1 { + margin-left: 0.25rem !important; + } + + .ms-sm-2 { + margin-left: 0.5rem !important; + } + + .ms-sm-3 { + margin-left: 1rem !important; + } + + .ms-sm-4 { + margin-left: 1.5rem !important; + } + + .ms-sm-5 { + margin-left: 3rem !important; + } + + .ms-sm-auto { + margin-left: auto !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .px-sm-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-sm-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-sm-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-sm-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-sm-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-sm-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-sm-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-sm-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-sm-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-sm-0 { + padding-top: 0 !important; + } + + .pt-sm-1 { + padding-top: 0.25rem !important; + } + + .pt-sm-2 { + padding-top: 0.5rem !important; + } + + .pt-sm-3 { + padding-top: 1rem !important; + } + + .pt-sm-4 { + padding-top: 1.5rem !important; + } + + .pt-sm-5 { + padding-top: 3rem !important; + } + + .pe-sm-0 { + padding-right: 0 !important; + } + + .pe-sm-1 { + padding-right: 0.25rem !important; + } + + .pe-sm-2 { + padding-right: 0.5rem !important; + } + + .pe-sm-3 { + padding-right: 1rem !important; + } + + .pe-sm-4 { + padding-right: 1.5rem !important; + } + + .pe-sm-5 { + padding-right: 3rem !important; + } + + .pb-sm-0 { + padding-bottom: 0 !important; + } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pb-sm-3 { + padding-bottom: 1rem !important; + } + + .pb-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pb-sm-5 { + padding-bottom: 3rem !important; + } + + .ps-sm-0 { + padding-left: 0 !important; + } + + .ps-sm-1 { + padding-left: 0.25rem !important; + } + + .ps-sm-2 { + padding-left: 0.5rem !important; + } + + .ps-sm-3 { + padding-left: 1rem !important; + } + + .ps-sm-4 { + padding-left: 1.5rem !important; + } + + .ps-sm-5 { + padding-left: 3rem !important; + } } + +@media (min-width: 768px) { + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-grid { + display: grid !important; + } + + .d-md-inline-grid { + display: inline-grid !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: flex !important; + } + + .d-md-inline-flex { + display: inline-flex !important; + } + + .d-md-none { + display: none !important; + } + + .flex-md-fill { + flex: 1 1 auto !important; + } + + .flex-md-row { + flex-direction: row !important; + } + + .flex-md-column { + flex-direction: column !important; + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-md-grow-0 { + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-md-wrap { + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-md-start { + justify-content: flex-start !important; + } + + .justify-content-md-end { + justify-content: flex-end !important; + } + + .justify-content-md-center { + justify-content: center !important; + } + + .justify-content-md-between { + justify-content: space-between !important; + } + + .justify-content-md-around { + justify-content: space-around !important; + } + + .justify-content-md-evenly { + justify-content: space-evenly !important; + } + + .align-items-md-start { + align-items: flex-start !important; + } + + .align-items-md-end { + align-items: flex-end !important; + } + + .align-items-md-center { + align-items: center !important; + } + + .align-items-md-baseline { + align-items: baseline !important; + } + + .align-items-md-stretch { + align-items: stretch !important; + } + + .align-content-md-start { + align-content: flex-start !important; + } + + .align-content-md-end { + align-content: flex-end !important; + } + + .align-content-md-center { + align-content: center !important; + } + + .align-content-md-between { + align-content: space-between !important; + } + + .align-content-md-around { + align-content: space-around !important; + } + + .align-content-md-stretch { + align-content: stretch !important; + } + + .align-self-md-auto { + align-self: auto !important; + } + + .align-self-md-start { + align-self: flex-start !important; + } + + .align-self-md-end { + align-self: flex-end !important; + } + + .align-self-md-center { + align-self: center !important; + } + + .align-self-md-baseline { + align-self: baseline !important; + } + + .align-self-md-stretch { + align-self: stretch !important; + } + + .order-md-first { + order: -1 !important; + } + + .order-md-0 { + order: 0 !important; + } + + .order-md-1 { + order: 1 !important; + } + + .order-md-2 { + order: 2 !important; + } + + .order-md-3 { + order: 3 !important; + } + + .order-md-4 { + order: 4 !important; + } + + .order-md-5 { + order: 5 !important; + } + + .order-md-last { + order: 6 !important; + } + + .m-md-0 { + margin: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mx-md-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-md-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-md-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-md-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-md-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-md-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-md-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-md-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-md-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-md-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-md-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-md-0 { + margin-top: 0 !important; + } + + .mt-md-1 { + margin-top: 0.25rem !important; + } + + .mt-md-2 { + margin-top: 0.5rem !important; + } + + .mt-md-3 { + margin-top: 1rem !important; + } + + .mt-md-4 { + margin-top: 1.5rem !important; + } + + .mt-md-5 { + margin-top: 3rem !important; + } + + .mt-md-auto { + margin-top: auto !important; + } + + .me-md-0 { + margin-right: 0 !important; + } + + .me-md-1 { + margin-right: 0.25rem !important; + } + + .me-md-2 { + margin-right: 0.5rem !important; + } + + .me-md-3 { + margin-right: 1rem !important; + } + + .me-md-4 { + margin-right: 1.5rem !important; + } + + .me-md-5 { + margin-right: 3rem !important; + } + + .me-md-auto { + margin-right: auto !important; + } + + .mb-md-0 { + margin-bottom: 0 !important; + } + + .mb-md-1 { + margin-bottom: 0.25rem !important; + } + + .mb-md-2 { + margin-bottom: 0.5rem !important; + } + + .mb-md-3 { + margin-bottom: 1rem !important; + } + + .mb-md-4 { + margin-bottom: 1.5rem !important; + } + + .mb-md-5 { + margin-bottom: 3rem !important; + } + + .mb-md-auto { + margin-bottom: auto !important; + } + + .ms-md-0 { + margin-left: 0 !important; + } + + .ms-md-1 { + margin-left: 0.25rem !important; + } + + .ms-md-2 { + margin-left: 0.5rem !important; + } + + .ms-md-3 { + margin-left: 1rem !important; + } + + .ms-md-4 { + margin-left: 1.5rem !important; + } + + .ms-md-5 { + margin-left: 3rem !important; + } + + .ms-md-auto { + margin-left: auto !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .px-md-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-md-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-md-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-md-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-md-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-md-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-md-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-md-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-md-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-md-0 { + padding-top: 0 !important; + } + + .pt-md-1 { + padding-top: 0.25rem !important; + } + + .pt-md-2 { + padding-top: 0.5rem !important; + } + + .pt-md-3 { + padding-top: 1rem !important; + } + + .pt-md-4 { + padding-top: 1.5rem !important; + } + + .pt-md-5 { + padding-top: 3rem !important; + } + + .pe-md-0 { + padding-right: 0 !important; + } + + .pe-md-1 { + padding-right: 0.25rem !important; + } + + .pe-md-2 { + padding-right: 0.5rem !important; + } + + .pe-md-3 { + padding-right: 1rem !important; + } + + .pe-md-4 { + padding-right: 1.5rem !important; + } + + .pe-md-5 { + padding-right: 3rem !important; + } + + .pb-md-0 { + padding-bottom: 0 !important; + } + + .pb-md-1 { + padding-bottom: 0.25rem !important; + } + + .pb-md-2 { + padding-bottom: 0.5rem !important; + } + + .pb-md-3 { + padding-bottom: 1rem !important; + } + + .pb-md-4 { + padding-bottom: 1.5rem !important; + } + + .pb-md-5 { + padding-bottom: 3rem !important; + } + + .ps-md-0 { + padding-left: 0 !important; + } + + .ps-md-1 { + padding-left: 0.25rem !important; + } + + .ps-md-2 { + padding-left: 0.5rem !important; + } + + .ps-md-3 { + padding-left: 1rem !important; + } + + .ps-md-4 { + padding-left: 1.5rem !important; + } + + .ps-md-5 { + padding-left: 3rem !important; + } +} + @media (min-width: 992px) { - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-lg-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-lg-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-lg-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-lg-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-lg-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-lg-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-right: 0 !important; - } - .me-lg-1 { - margin-right: 0.25rem !important; - } - .me-lg-2 { - margin-right: 0.5rem !important; - } - .me-lg-3 { - margin-right: 1rem !important; - } - .me-lg-4 { - margin-right: 1.5rem !important; - } - .me-lg-5 { - margin-right: 3rem !important; - } - .me-lg-auto { - margin-right: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-left: 0 !important; - } - .ms-lg-1 { - margin-left: 0.25rem !important; - } - .ms-lg-2 { - margin-left: 0.5rem !important; - } - .ms-lg-3 { - margin-left: 1rem !important; - } - .ms-lg-4 { - margin-left: 1.5rem !important; - } - .ms-lg-5 { - margin-left: 3rem !important; - } - .ms-lg-auto { - margin-left: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-lg-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-lg-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-lg-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-lg-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-lg-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-right: 0 !important; - } - .pe-lg-1 { - padding-right: 0.25rem !important; - } - .pe-lg-2 { - padding-right: 0.5rem !important; - } - .pe-lg-3 { - padding-right: 1rem !important; - } - .pe-lg-4 { - padding-right: 1.5rem !important; - } - .pe-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-left: 0 !important; - } - .ps-lg-1 { - padding-left: 0.25rem !important; - } - .ps-lg-2 { - padding-left: 0.5rem !important; - } - .ps-lg-3 { - padding-left: 1rem !important; - } - .ps-lg-4 { - padding-left: 1.5rem !important; - } - .ps-lg-5 { - padding-left: 3rem !important; - } + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-grid { + display: grid !important; + } + + .d-lg-inline-grid { + display: inline-grid !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: flex !important; + } + + .d-lg-inline-flex { + display: inline-flex !important; + } + + .d-lg-none { + display: none !important; + } + + .flex-lg-fill { + flex: 1 1 auto !important; + } + + .flex-lg-row { + flex-direction: row !important; + } + + .flex-lg-column { + flex-direction: column !important; + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-lg-wrap { + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-lg-start { + justify-content: flex-start !important; + } + + .justify-content-lg-end { + justify-content: flex-end !important; + } + + .justify-content-lg-center { + justify-content: center !important; + } + + .justify-content-lg-between { + justify-content: space-between !important; + } + + .justify-content-lg-around { + justify-content: space-around !important; + } + + .justify-content-lg-evenly { + justify-content: space-evenly !important; + } + + .align-items-lg-start { + align-items: flex-start !important; + } + + .align-items-lg-end { + align-items: flex-end !important; + } + + .align-items-lg-center { + align-items: center !important; + } + + .align-items-lg-baseline { + align-items: baseline !important; + } + + .align-items-lg-stretch { + align-items: stretch !important; + } + + .align-content-lg-start { + align-content: flex-start !important; + } + + .align-content-lg-end { + align-content: flex-end !important; + } + + .align-content-lg-center { + align-content: center !important; + } + + .align-content-lg-between { + align-content: space-between !important; + } + + .align-content-lg-around { + align-content: space-around !important; + } + + .align-content-lg-stretch { + align-content: stretch !important; + } + + .align-self-lg-auto { + align-self: auto !important; + } + + .align-self-lg-start { + align-self: flex-start !important; + } + + .align-self-lg-end { + align-self: flex-end !important; + } + + .align-self-lg-center { + align-self: center !important; + } + + .align-self-lg-baseline { + align-self: baseline !important; + } + + .align-self-lg-stretch { + align-self: stretch !important; + } + + .order-lg-first { + order: -1 !important; + } + + .order-lg-0 { + order: 0 !important; + } + + .order-lg-1 { + order: 1 !important; + } + + .order-lg-2 { + order: 2 !important; + } + + .order-lg-3 { + order: 3 !important; + } + + .order-lg-4 { + order: 4 !important; + } + + .order-lg-5 { + order: 5 !important; + } + + .order-lg-last { + order: 6 !important; + } + + .m-lg-0 { + margin: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mx-lg-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-lg-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-lg-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-lg-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-lg-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-lg-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-lg-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-lg-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-lg-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-lg-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-lg-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-lg-0 { + margin-top: 0 !important; + } + + .mt-lg-1 { + margin-top: 0.25rem !important; + } + + .mt-lg-2 { + margin-top: 0.5rem !important; + } + + .mt-lg-3 { + margin-top: 1rem !important; + } + + .mt-lg-4 { + margin-top: 1.5rem !important; + } + + .mt-lg-5 { + margin-top: 3rem !important; + } + + .mt-lg-auto { + margin-top: auto !important; + } + + .me-lg-0 { + margin-right: 0 !important; + } + + .me-lg-1 { + margin-right: 0.25rem !important; + } + + .me-lg-2 { + margin-right: 0.5rem !important; + } + + .me-lg-3 { + margin-right: 1rem !important; + } + + .me-lg-4 { + margin-right: 1.5rem !important; + } + + .me-lg-5 { + margin-right: 3rem !important; + } + + .me-lg-auto { + margin-right: auto !important; + } + + .mb-lg-0 { + margin-bottom: 0 !important; + } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; + } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; + } + + .mb-lg-3 { + margin-bottom: 1rem !important; + } + + .mb-lg-4 { + margin-bottom: 1.5rem !important; + } + + .mb-lg-5 { + margin-bottom: 3rem !important; + } + + .mb-lg-auto { + margin-bottom: auto !important; + } + + .ms-lg-0 { + margin-left: 0 !important; + } + + .ms-lg-1 { + margin-left: 0.25rem !important; + } + + .ms-lg-2 { + margin-left: 0.5rem !important; + } + + .ms-lg-3 { + margin-left: 1rem !important; + } + + .ms-lg-4 { + margin-left: 1.5rem !important; + } + + .ms-lg-5 { + margin-left: 3rem !important; + } + + .ms-lg-auto { + margin-left: auto !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .px-lg-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-lg-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-lg-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-lg-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-lg-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-lg-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-lg-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-lg-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-lg-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-lg-0 { + padding-top: 0 !important; + } + + .pt-lg-1 { + padding-top: 0.25rem !important; + } + + .pt-lg-2 { + padding-top: 0.5rem !important; + } + + .pt-lg-3 { + padding-top: 1rem !important; + } + + .pt-lg-4 { + padding-top: 1.5rem !important; + } + + .pt-lg-5 { + padding-top: 3rem !important; + } + + .pe-lg-0 { + padding-right: 0 !important; + } + + .pe-lg-1 { + padding-right: 0.25rem !important; + } + + .pe-lg-2 { + padding-right: 0.5rem !important; + } + + .pe-lg-3 { + padding-right: 1rem !important; + } + + .pe-lg-4 { + padding-right: 1.5rem !important; + } + + .pe-lg-5 { + padding-right: 3rem !important; + } + + .pb-lg-0 { + padding-bottom: 0 !important; + } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pb-lg-3 { + padding-bottom: 1rem !important; + } + + .pb-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pb-lg-5 { + padding-bottom: 3rem !important; + } + + .ps-lg-0 { + padding-left: 0 !important; + } + + .ps-lg-1 { + padding-left: 0.25rem !important; + } + + .ps-lg-2 { + padding-left: 0.5rem !important; + } + + .ps-lg-3 { + padding-left: 1rem !important; + } + + .ps-lg-4 { + padding-left: 1.5rem !important; + } + + .ps-lg-5 { + padding-left: 3rem !important; + } } + @media (min-width: 1200px) { - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-right: 0 !important; - } - .me-xl-1 { - margin-right: 0.25rem !important; - } - .me-xl-2 { - margin-right: 0.5rem !important; - } - .me-xl-3 { - margin-right: 1rem !important; - } - .me-xl-4 { - margin-right: 1.5rem !important; - } - .me-xl-5 { - margin-right: 3rem !important; - } - .me-xl-auto { - margin-right: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-left: 0 !important; - } - .ms-xl-1 { - margin-left: 0.25rem !important; - } - .ms-xl-2 { - margin-left: 0.5rem !important; - } - .ms-xl-3 { - margin-left: 1rem !important; - } - .ms-xl-4 { - margin-left: 1.5rem !important; - } - .ms-xl-5 { - margin-left: 3rem !important; - } - .ms-xl-auto { - margin-left: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-right: 0 !important; - } - .pe-xl-1 { - padding-right: 0.25rem !important; - } - .pe-xl-2 { - padding-right: 0.5rem !important; - } - .pe-xl-3 { - padding-right: 1rem !important; - } - .pe-xl-4 { - padding-right: 1.5rem !important; - } - .pe-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-left: 0 !important; - } - .ps-xl-1 { - padding-left: 0.25rem !important; - } - .ps-xl-2 { - padding-left: 0.5rem !important; - } - .ps-xl-3 { - padding-left: 1rem !important; - } - .ps-xl-4 { - padding-left: 1.5rem !important; - } - .ps-xl-5 { - padding-left: 3rem !important; - } + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-grid { + display: grid !important; + } + + .d-xl-inline-grid { + display: inline-grid !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: flex !important; + } + + .d-xl-inline-flex { + display: inline-flex !important; + } + + .d-xl-none { + display: none !important; + } + + .flex-xl-fill { + flex: 1 1 auto !important; + } + + .flex-xl-row { + flex-direction: row !important; + } + + .flex-xl-column { + flex-direction: column !important; + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xl-wrap { + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xl-start { + justify-content: flex-start !important; + } + + .justify-content-xl-end { + justify-content: flex-end !important; + } + + .justify-content-xl-center { + justify-content: center !important; + } + + .justify-content-xl-between { + justify-content: space-between !important; + } + + .justify-content-xl-around { + justify-content: space-around !important; + } + + .justify-content-xl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xl-start { + align-items: flex-start !important; + } + + .align-items-xl-end { + align-items: flex-end !important; + } + + .align-items-xl-center { + align-items: center !important; + } + + .align-items-xl-baseline { + align-items: baseline !important; + } + + .align-items-xl-stretch { + align-items: stretch !important; + } + + .align-content-xl-start { + align-content: flex-start !important; + } + + .align-content-xl-end { + align-content: flex-end !important; + } + + .align-content-xl-center { + align-content: center !important; + } + + .align-content-xl-between { + align-content: space-between !important; + } + + .align-content-xl-around { + align-content: space-around !important; + } + + .align-content-xl-stretch { + align-content: stretch !important; + } + + .align-self-xl-auto { + align-self: auto !important; + } + + .align-self-xl-start { + align-self: flex-start !important; + } + + .align-self-xl-end { + align-self: flex-end !important; + } + + .align-self-xl-center { + align-self: center !important; + } + + .align-self-xl-baseline { + align-self: baseline !important; + } + + .align-self-xl-stretch { + align-self: stretch !important; + } + + .order-xl-first { + order: -1 !important; + } + + .order-xl-0 { + order: 0 !important; + } + + .order-xl-1 { + order: 1 !important; + } + + .order-xl-2 { + order: 2 !important; + } + + .order-xl-3 { + order: 3 !important; + } + + .order-xl-4 { + order: 4 !important; + } + + .order-xl-5 { + order: 5 !important; + } + + .order-xl-last { + order: 6 !important; + } + + .m-xl-0 { + margin: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mx-xl-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-xl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-xl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-xl-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-xl-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-xl-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-xl-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xl-0 { + margin-top: 0 !important; + } + + .mt-xl-1 { + margin-top: 0.25rem !important; + } + + .mt-xl-2 { + margin-top: 0.5rem !important; + } + + .mt-xl-3 { + margin-top: 1rem !important; + } + + .mt-xl-4 { + margin-top: 1.5rem !important; + } + + .mt-xl-5 { + margin-top: 3rem !important; + } + + .mt-xl-auto { + margin-top: auto !important; + } + + .me-xl-0 { + margin-right: 0 !important; + } + + .me-xl-1 { + margin-right: 0.25rem !important; + } + + .me-xl-2 { + margin-right: 0.5rem !important; + } + + .me-xl-3 { + margin-right: 1rem !important; + } + + .me-xl-4 { + margin-right: 1.5rem !important; + } + + .me-xl-5 { + margin-right: 3rem !important; + } + + .me-xl-auto { + margin-right: auto !important; + } + + .mb-xl-0 { + margin-bottom: 0 !important; + } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xl-3 { + margin-bottom: 1rem !important; + } + + .mb-xl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xl-5 { + margin-bottom: 3rem !important; + } + + .mb-xl-auto { + margin-bottom: auto !important; + } + + .ms-xl-0 { + margin-left: 0 !important; + } + + .ms-xl-1 { + margin-left: 0.25rem !important; + } + + .ms-xl-2 { + margin-left: 0.5rem !important; + } + + .ms-xl-3 { + margin-left: 1rem !important; + } + + .ms-xl-4 { + margin-left: 1.5rem !important; + } + + .ms-xl-5 { + margin-left: 3rem !important; + } + + .ms-xl-auto { + margin-left: auto !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .px-xl-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-xl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-xl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-xl-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-xl-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-xl-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xl-0 { + padding-top: 0 !important; + } + + .pt-xl-1 { + padding-top: 0.25rem !important; + } + + .pt-xl-2 { + padding-top: 0.5rem !important; + } + + .pt-xl-3 { + padding-top: 1rem !important; + } + + .pt-xl-4 { + padding-top: 1.5rem !important; + } + + .pt-xl-5 { + padding-top: 3rem !important; + } + + .pe-xl-0 { + padding-right: 0 !important; + } + + .pe-xl-1 { + padding-right: 0.25rem !important; + } + + .pe-xl-2 { + padding-right: 0.5rem !important; + } + + .pe-xl-3 { + padding-right: 1rem !important; + } + + .pe-xl-4 { + padding-right: 1.5rem !important; + } + + .pe-xl-5 { + padding-right: 3rem !important; + } + + .pb-xl-0 { + padding-bottom: 0 !important; + } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xl-3 { + padding-bottom: 1rem !important; + } + + .pb-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xl-5 { + padding-bottom: 3rem !important; + } + + .ps-xl-0 { + padding-left: 0 !important; + } + + .ps-xl-1 { + padding-left: 0.25rem !important; + } + + .ps-xl-2 { + padding-left: 0.5rem !important; + } + + .ps-xl-3 { + padding-left: 1rem !important; + } + + .ps-xl-4 { + padding-left: 1.5rem !important; + } + + .ps-xl-5 { + padding-left: 3rem !important; + } } + @media (min-width: 1400px) { - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xxl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xxl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xxl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xxl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xxl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xxl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-right: 0 !important; - } - .me-xxl-1 { - margin-right: 0.25rem !important; - } - .me-xxl-2 { - margin-right: 0.5rem !important; - } - .me-xxl-3 { - margin-right: 1rem !important; - } - .me-xxl-4 { - margin-right: 1.5rem !important; - } - .me-xxl-5 { - margin-right: 3rem !important; - } - .me-xxl-auto { - margin-right: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-left: 0 !important; - } - .ms-xxl-1 { - margin-left: 0.25rem !important; - } - .ms-xxl-2 { - margin-left: 0.5rem !important; - } - .ms-xxl-3 { - margin-left: 1rem !important; - } - .ms-xxl-4 { - margin-left: 1.5rem !important; - } - .ms-xxl-5 { - margin-left: 3rem !important; - } - .ms-xxl-auto { - margin-left: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xxl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xxl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xxl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xxl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xxl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-right: 0 !important; - } - .pe-xxl-1 { - padding-right: 0.25rem !important; - } - .pe-xxl-2 { - padding-right: 0.5rem !important; - } - .pe-xxl-3 { - padding-right: 1rem !important; - } - .pe-xxl-4 { - padding-right: 1.5rem !important; - } - .pe-xxl-5 { - padding-right: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-left: 0 !important; - } - .ps-xxl-1 { - padding-left: 0.25rem !important; - } - .ps-xxl-2 { - padding-left: 0.5rem !important; - } - .ps-xxl-3 { - padding-left: 1rem !important; - } - .ps-xxl-4 { - padding-left: 1.5rem !important; - } - .ps-xxl-5 { - padding-left: 3rem !important; - } + .d-xxl-inline { + display: inline !important; + } + + .d-xxl-inline-block { + display: inline-block !important; + } + + .d-xxl-block { + display: block !important; + } + + .d-xxl-grid { + display: grid !important; + } + + .d-xxl-inline-grid { + display: inline-grid !important; + } + + .d-xxl-table { + display: table !important; + } + + .d-xxl-table-row { + display: table-row !important; + } + + .d-xxl-table-cell { + display: table-cell !important; + } + + .d-xxl-flex { + display: flex !important; + } + + .d-xxl-inline-flex { + display: inline-flex !important; + } + + .d-xxl-none { + display: none !important; + } + + .flex-xxl-fill { + flex: 1 1 auto !important; + } + + .flex-xxl-row { + flex-direction: row !important; + } + + .flex-xxl-column { + flex-direction: column !important; + } + + .flex-xxl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xxl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xxl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xxl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xxl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xxl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xxl-wrap { + flex-wrap: wrap !important; + } + + .flex-xxl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xxl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xxl-start { + justify-content: flex-start !important; + } + + .justify-content-xxl-end { + justify-content: flex-end !important; + } + + .justify-content-xxl-center { + justify-content: center !important; + } + + .justify-content-xxl-between { + justify-content: space-between !important; + } + + .justify-content-xxl-around { + justify-content: space-around !important; + } + + .justify-content-xxl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xxl-start { + align-items: flex-start !important; + } + + .align-items-xxl-end { + align-items: flex-end !important; + } + + .align-items-xxl-center { + align-items: center !important; + } + + .align-items-xxl-baseline { + align-items: baseline !important; + } + + .align-items-xxl-stretch { + align-items: stretch !important; + } + + .align-content-xxl-start { + align-content: flex-start !important; + } + + .align-content-xxl-end { + align-content: flex-end !important; + } + + .align-content-xxl-center { + align-content: center !important; + } + + .align-content-xxl-between { + align-content: space-between !important; + } + + .align-content-xxl-around { + align-content: space-around !important; + } + + .align-content-xxl-stretch { + align-content: stretch !important; + } + + .align-self-xxl-auto { + align-self: auto !important; + } + + .align-self-xxl-start { + align-self: flex-start !important; + } + + .align-self-xxl-end { + align-self: flex-end !important; + } + + .align-self-xxl-center { + align-self: center !important; + } + + .align-self-xxl-baseline { + align-self: baseline !important; + } + + .align-self-xxl-stretch { + align-self: stretch !important; + } + + .order-xxl-first { + order: -1 !important; + } + + .order-xxl-0 { + order: 0 !important; + } + + .order-xxl-1 { + order: 1 !important; + } + + .order-xxl-2 { + order: 2 !important; + } + + .order-xxl-3 { + order: 3 !important; + } + + .order-xxl-4 { + order: 4 !important; + } + + .order-xxl-5 { + order: 5 !important; + } + + .order-xxl-last { + order: 6 !important; + } + + .m-xxl-0 { + margin: 0 !important; + } + + .m-xxl-1 { + margin: 0.25rem !important; + } + + .m-xxl-2 { + margin: 0.5rem !important; + } + + .m-xxl-3 { + margin: 1rem !important; + } + + .m-xxl-4 { + margin: 1.5rem !important; + } + + .m-xxl-5 { + margin: 3rem !important; + } + + .m-xxl-auto { + margin: auto !important; + } + + .mx-xxl-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-xxl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-xxl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-xxl-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-xxl-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-xxl-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-xxl-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-xxl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xxl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xxl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xxl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xxl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xxl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xxl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xxl-0 { + margin-top: 0 !important; + } + + .mt-xxl-1 { + margin-top: 0.25rem !important; + } + + .mt-xxl-2 { + margin-top: 0.5rem !important; + } + + .mt-xxl-3 { + margin-top: 1rem !important; + } + + .mt-xxl-4 { + margin-top: 1.5rem !important; + } + + .mt-xxl-5 { + margin-top: 3rem !important; + } + + .mt-xxl-auto { + margin-top: auto !important; + } + + .me-xxl-0 { + margin-right: 0 !important; + } + + .me-xxl-1 { + margin-right: 0.25rem !important; + } + + .me-xxl-2 { + margin-right: 0.5rem !important; + } + + .me-xxl-3 { + margin-right: 1rem !important; + } + + .me-xxl-4 { + margin-right: 1.5rem !important; + } + + .me-xxl-5 { + margin-right: 3rem !important; + } + + .me-xxl-auto { + margin-right: auto !important; + } + + .mb-xxl-0 { + margin-bottom: 0 !important; + } + + .mb-xxl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xxl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xxl-3 { + margin-bottom: 1rem !important; + } + + .mb-xxl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xxl-5 { + margin-bottom: 3rem !important; + } + + .mb-xxl-auto { + margin-bottom: auto !important; + } + + .ms-xxl-0 { + margin-left: 0 !important; + } + + .ms-xxl-1 { + margin-left: 0.25rem !important; + } + + .ms-xxl-2 { + margin-left: 0.5rem !important; + } + + .ms-xxl-3 { + margin-left: 1rem !important; + } + + .ms-xxl-4 { + margin-left: 1.5rem !important; + } + + .ms-xxl-5 { + margin-left: 3rem !important; + } + + .ms-xxl-auto { + margin-left: auto !important; + } + + .p-xxl-0 { + padding: 0 !important; + } + + .p-xxl-1 { + padding: 0.25rem !important; + } + + .p-xxl-2 { + padding: 0.5rem !important; + } + + .p-xxl-3 { + padding: 1rem !important; + } + + .p-xxl-4 { + padding: 1.5rem !important; + } + + .p-xxl-5 { + padding: 3rem !important; + } + + .px-xxl-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-xxl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-xxl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-xxl-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-xxl-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-xxl-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-xxl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xxl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xxl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xxl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xxl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xxl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xxl-0 { + padding-top: 0 !important; + } + + .pt-xxl-1 { + padding-top: 0.25rem !important; + } + + .pt-xxl-2 { + padding-top: 0.5rem !important; + } + + .pt-xxl-3 { + padding-top: 1rem !important; + } + + .pt-xxl-4 { + padding-top: 1.5rem !important; + } + + .pt-xxl-5 { + padding-top: 3rem !important; + } + + .pe-xxl-0 { + padding-right: 0 !important; + } + + .pe-xxl-1 { + padding-right: 0.25rem !important; + } + + .pe-xxl-2 { + padding-right: 0.5rem !important; + } + + .pe-xxl-3 { + padding-right: 1rem !important; + } + + .pe-xxl-4 { + padding-right: 1.5rem !important; + } + + .pe-xxl-5 { + padding-right: 3rem !important; + } + + .pb-xxl-0 { + padding-bottom: 0 !important; + } + + .pb-xxl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xxl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xxl-3 { + padding-bottom: 1rem !important; + } + + .pb-xxl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xxl-5 { + padding-bottom: 3rem !important; + } + + .ps-xxl-0 { + padding-left: 0 !important; + } + + .ps-xxl-1 { + padding-left: 0.25rem !important; + } + + .ps-xxl-2 { + padding-left: 0.5rem !important; + } + + .ps-xxl-3 { + padding-left: 1rem !important; + } + + .ps-xxl-4 { + padding-left: 1.5rem !important; + } + + .ps-xxl-5 { + padding-left: 3rem !important; + } } + @media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-grid { + display: grid !important; + } + + .d-print-inline-grid { + display: inline-grid !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: flex !important; + } + + .d-print-inline-flex { + display: inline-flex !important; + } + + .d-print-none { + display: none !important; + } } -/*# sourceMappingURL=bootstrap-grid.css.map */ \ No newline at end of file +/*# sourceMappingURL=bootstrap-grid.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css index 1a5d65630b..af7bdc79e6 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css @@ -10,4075 +10,5042 @@ .container-lg, .container-md, .container-sm { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - width: 100%; - padding-left: calc(var(--bs-gutter-x) * 0.5); - padding-right: calc(var(--bs-gutter-x) * 0.5); - margin-left: auto; - margin-right: auto; + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + width: 100%; + padding-left: calc(var(--bs-gutter-x) * 0.5); + padding-right: calc(var(--bs-gutter-x) * 0.5); + margin-left: auto; + margin-right: auto; } @media (min-width: 576px) { - .container-sm, .container { - max-width: 540px; - } + .container-sm, .container { + max-width: 540px; + } } + @media (min-width: 768px) { - .container-md, .container-sm, .container { - max-width: 720px; - } + .container-md, .container-sm, .container { + max-width: 720px; + } } + @media (min-width: 992px) { - .container-lg, .container-md, .container-sm, .container { - max-width: 960px; - } + .container-lg, .container-md, .container-sm, .container { + max-width: 960px; + } } + @media (min-width: 1200px) { - .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1140px; - } + .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1140px; + } } + @media (min-width: 1400px) { - .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1320px; - } + .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1320px; + } } + :root { - --bs-breakpoint-xs: 0; - --bs-breakpoint-sm: 576px; - --bs-breakpoint-md: 768px; - --bs-breakpoint-lg: 992px; - --bs-breakpoint-xl: 1200px; - --bs-breakpoint-xxl: 1400px; + --bs-breakpoint-xs: 0; + --bs-breakpoint-sm: 576px; + --bs-breakpoint-md: 768px; + --bs-breakpoint-lg: 992px; + --bs-breakpoint-xl: 1200px; + --bs-breakpoint-xxl: 1400px; } .row { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - display: flex; - flex-wrap: wrap; - margin-top: calc(-1 * var(--bs-gutter-y)); - margin-left: calc(-0.5 * var(--bs-gutter-x)); - margin-right: calc(-0.5 * var(--bs-gutter-x)); + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + display: flex; + flex-wrap: wrap; + margin-top: calc(-1 * var(--bs-gutter-y)); + margin-left: calc(-0.5 * var(--bs-gutter-x)); + margin-right: calc(-0.5 * var(--bs-gutter-x)); } + .row > * { - box-sizing: border-box; - flex-shrink: 0; - width: 100%; - max-width: 100%; - padding-left: calc(var(--bs-gutter-x) * 0.5); - padding-right: calc(var(--bs-gutter-x) * 0.5); - margin-top: var(--bs-gutter-y); + box-sizing: border-box; + flex-shrink: 0; + width: 100%; + max-width: 100%; + padding-left: calc(var(--bs-gutter-x) * 0.5); + padding-right: calc(var(--bs-gutter-x) * 0.5); + margin-top: var(--bs-gutter-y); } .col { - flex: 1 0 0%; + flex: 1 0 0%; } .row-cols-auto > * { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .row-cols-1 > * { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .row-cols-2 > * { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .row-cols-3 > * { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .row-cols-4 > * { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .row-cols-5 > * { - flex: 0 0 auto; - width: 20%; + flex: 0 0 auto; + width: 20%; } .row-cols-6 > * { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-auto { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .col-1 { - flex: 0 0 auto; - width: 8.33333333%; + flex: 0 0 auto; + width: 8.33333333%; } .col-2 { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-3 { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .col-4 { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .col-5 { - flex: 0 0 auto; - width: 41.66666667%; + flex: 0 0 auto; + width: 41.66666667%; } .col-6 { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .col-7 { - flex: 0 0 auto; - width: 58.33333333%; + flex: 0 0 auto; + width: 58.33333333%; } .col-8 { - flex: 0 0 auto; - width: 66.66666667%; + flex: 0 0 auto; + width: 66.66666667%; } .col-9 { - flex: 0 0 auto; - width: 75%; + flex: 0 0 auto; + width: 75%; } .col-10 { - flex: 0 0 auto; - width: 83.33333333%; + flex: 0 0 auto; + width: 83.33333333%; } .col-11 { - flex: 0 0 auto; - width: 91.66666667%; + flex: 0 0 auto; + width: 91.66666667%; } .col-12 { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .offset-1 { - margin-right: 8.33333333%; + margin-right: 8.33333333%; } .offset-2 { - margin-right: 16.66666667%; + margin-right: 16.66666667%; } .offset-3 { - margin-right: 25%; + margin-right: 25%; } .offset-4 { - margin-right: 33.33333333%; + margin-right: 33.33333333%; } .offset-5 { - margin-right: 41.66666667%; + margin-right: 41.66666667%; } .offset-6 { - margin-right: 50%; + margin-right: 50%; } .offset-7 { - margin-right: 58.33333333%; + margin-right: 58.33333333%; } .offset-8 { - margin-right: 66.66666667%; + margin-right: 66.66666667%; } .offset-9 { - margin-right: 75%; + margin-right: 75%; } .offset-10 { - margin-right: 83.33333333%; + margin-right: 83.33333333%; } .offset-11 { - margin-right: 91.66666667%; + margin-right: 91.66666667%; } .g-0, .gx-0 { - --bs-gutter-x: 0; + --bs-gutter-x: 0; } .g-0, .gy-0 { - --bs-gutter-y: 0; + --bs-gutter-y: 0; } .g-1, .gx-1 { - --bs-gutter-x: 0.25rem; + --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { - --bs-gutter-y: 0.25rem; + --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { - --bs-gutter-x: 0.5rem; + --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { - --bs-gutter-y: 0.5rem; + --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { - --bs-gutter-x: 1rem; + --bs-gutter-x: 1rem; } .g-3, .gy-3 { - --bs-gutter-y: 1rem; + --bs-gutter-y: 1rem; } .g-4, .gx-4 { - --bs-gutter-x: 1.5rem; + --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { - --bs-gutter-y: 1.5rem; + --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { - --bs-gutter-x: 3rem; + --bs-gutter-x: 3rem; } .g-5, .gy-5 { - --bs-gutter-y: 3rem; + --bs-gutter-y: 3rem; } @media (min-width: 576px) { - .col-sm { - flex: 1 0 0%; - } - .row-cols-sm-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-sm-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-sm-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-sm-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-sm-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-sm-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-sm-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-auto { - flex: 0 0 auto; - width: auto; - } - .col-sm-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-sm-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-3 { - flex: 0 0 auto; - width: 25%; - } - .col-sm-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-sm-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-sm-6 { - flex: 0 0 auto; - width: 50%; - } - .col-sm-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-sm-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-sm-9 { - flex: 0 0 auto; - width: 75%; - } - .col-sm-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-sm-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-sm-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-sm-0 { - margin-right: 0; - } - .offset-sm-1 { - margin-right: 8.33333333%; - } - .offset-sm-2 { - margin-right: 16.66666667%; - } - .offset-sm-3 { - margin-right: 25%; - } - .offset-sm-4 { - margin-right: 33.33333333%; - } - .offset-sm-5 { - margin-right: 41.66666667%; - } - .offset-sm-6 { - margin-right: 50%; - } - .offset-sm-7 { - margin-right: 58.33333333%; - } - .offset-sm-8 { - margin-right: 66.66666667%; - } - .offset-sm-9 { - margin-right: 75%; - } - .offset-sm-10 { - margin-right: 83.33333333%; - } - .offset-sm-11 { - margin-right: 91.66666667%; - } - .g-sm-0, - .gx-sm-0 { - --bs-gutter-x: 0; - } - .g-sm-0, - .gy-sm-0 { - --bs-gutter-y: 0; - } - .g-sm-1, - .gx-sm-1 { - --bs-gutter-x: 0.25rem; - } - .g-sm-1, - .gy-sm-1 { - --bs-gutter-y: 0.25rem; - } - .g-sm-2, - .gx-sm-2 { - --bs-gutter-x: 0.5rem; - } - .g-sm-2, - .gy-sm-2 { - --bs-gutter-y: 0.5rem; - } - .g-sm-3, - .gx-sm-3 { - --bs-gutter-x: 1rem; - } - .g-sm-3, - .gy-sm-3 { - --bs-gutter-y: 1rem; - } - .g-sm-4, - .gx-sm-4 { - --bs-gutter-x: 1.5rem; - } - .g-sm-4, - .gy-sm-4 { - --bs-gutter-y: 1.5rem; - } - .g-sm-5, - .gx-sm-5 { - --bs-gutter-x: 3rem; - } - .g-sm-5, - .gy-sm-5 { - --bs-gutter-y: 3rem; - } + .col-sm { + flex: 1 0 0%; + } + + .row-cols-sm-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-sm-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-sm-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-sm-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-sm-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-sm-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-sm-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + } + + .col-sm-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-sm-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-sm-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-sm-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-sm-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-sm-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-sm-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-sm-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-sm-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-sm-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-sm-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-sm-0 { + margin-right: 0; + } + + .offset-sm-1 { + margin-right: 8.33333333%; + } + + .offset-sm-2 { + margin-right: 16.66666667%; + } + + .offset-sm-3 { + margin-right: 25%; + } + + .offset-sm-4 { + margin-right: 33.33333333%; + } + + .offset-sm-5 { + margin-right: 41.66666667%; + } + + .offset-sm-6 { + margin-right: 50%; + } + + .offset-sm-7 { + margin-right: 58.33333333%; + } + + .offset-sm-8 { + margin-right: 66.66666667%; + } + + .offset-sm-9 { + margin-right: 75%; + } + + .offset-sm-10 { + margin-right: 83.33333333%; + } + + .offset-sm-11 { + margin-right: 91.66666667%; + } + + .g-sm-0, + .gx-sm-0 { + --bs-gutter-x: 0; + } + + .g-sm-0, + .gy-sm-0 { + --bs-gutter-y: 0; + } + + .g-sm-1, + .gx-sm-1 { + --bs-gutter-x: 0.25rem; + } + + .g-sm-1, + .gy-sm-1 { + --bs-gutter-y: 0.25rem; + } + + .g-sm-2, + .gx-sm-2 { + --bs-gutter-x: 0.5rem; + } + + .g-sm-2, + .gy-sm-2 { + --bs-gutter-y: 0.5rem; + } + + .g-sm-3, + .gx-sm-3 { + --bs-gutter-x: 1rem; + } + + .g-sm-3, + .gy-sm-3 { + --bs-gutter-y: 1rem; + } + + .g-sm-4, + .gx-sm-4 { + --bs-gutter-x: 1.5rem; + } + + .g-sm-4, + .gy-sm-4 { + --bs-gutter-y: 1.5rem; + } + + .g-sm-5, + .gx-sm-5 { + --bs-gutter-x: 3rem; + } + + .g-sm-5, + .gy-sm-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 768px) { - .col-md { - flex: 1 0 0%; - } - .row-cols-md-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-md-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-md-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-md-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-md-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-md-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-md-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-auto { - flex: 0 0 auto; - width: auto; - } - .col-md-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-md-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-3 { - flex: 0 0 auto; - width: 25%; - } - .col-md-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-md-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-md-6 { - flex: 0 0 auto; - width: 50%; - } - .col-md-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-md-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-md-9 { - flex: 0 0 auto; - width: 75%; - } - .col-md-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-md-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-md-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-md-0 { - margin-right: 0; - } - .offset-md-1 { - margin-right: 8.33333333%; - } - .offset-md-2 { - margin-right: 16.66666667%; - } - .offset-md-3 { - margin-right: 25%; - } - .offset-md-4 { - margin-right: 33.33333333%; - } - .offset-md-5 { - margin-right: 41.66666667%; - } - .offset-md-6 { - margin-right: 50%; - } - .offset-md-7 { - margin-right: 58.33333333%; - } - .offset-md-8 { - margin-right: 66.66666667%; - } - .offset-md-9 { - margin-right: 75%; - } - .offset-md-10 { - margin-right: 83.33333333%; - } - .offset-md-11 { - margin-right: 91.66666667%; - } - .g-md-0, - .gx-md-0 { - --bs-gutter-x: 0; - } - .g-md-0, - .gy-md-0 { - --bs-gutter-y: 0; - } - .g-md-1, - .gx-md-1 { - --bs-gutter-x: 0.25rem; - } - .g-md-1, - .gy-md-1 { - --bs-gutter-y: 0.25rem; - } - .g-md-2, - .gx-md-2 { - --bs-gutter-x: 0.5rem; - } - .g-md-2, - .gy-md-2 { - --bs-gutter-y: 0.5rem; - } - .g-md-3, - .gx-md-3 { - --bs-gutter-x: 1rem; - } - .g-md-3, - .gy-md-3 { - --bs-gutter-y: 1rem; - } - .g-md-4, - .gx-md-4 { - --bs-gutter-x: 1.5rem; - } - .g-md-4, - .gy-md-4 { - --bs-gutter-y: 1.5rem; - } - .g-md-5, - .gx-md-5 { - --bs-gutter-x: 3rem; - } - .g-md-5, - .gy-md-5 { - --bs-gutter-y: 3rem; - } + .col-md { + flex: 1 0 0%; + } + + .row-cols-md-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-md-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-md-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-md-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-md-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-md-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-md-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + } + + .col-md-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-md-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-md-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-md-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-md-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-md-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-md-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-md-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-md-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-md-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-md-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-md-0 { + margin-right: 0; + } + + .offset-md-1 { + margin-right: 8.33333333%; + } + + .offset-md-2 { + margin-right: 16.66666667%; + } + + .offset-md-3 { + margin-right: 25%; + } + + .offset-md-4 { + margin-right: 33.33333333%; + } + + .offset-md-5 { + margin-right: 41.66666667%; + } + + .offset-md-6 { + margin-right: 50%; + } + + .offset-md-7 { + margin-right: 58.33333333%; + } + + .offset-md-8 { + margin-right: 66.66666667%; + } + + .offset-md-9 { + margin-right: 75%; + } + + .offset-md-10 { + margin-right: 83.33333333%; + } + + .offset-md-11 { + margin-right: 91.66666667%; + } + + .g-md-0, + .gx-md-0 { + --bs-gutter-x: 0; + } + + .g-md-0, + .gy-md-0 { + --bs-gutter-y: 0; + } + + .g-md-1, + .gx-md-1 { + --bs-gutter-x: 0.25rem; + } + + .g-md-1, + .gy-md-1 { + --bs-gutter-y: 0.25rem; + } + + .g-md-2, + .gx-md-2 { + --bs-gutter-x: 0.5rem; + } + + .g-md-2, + .gy-md-2 { + --bs-gutter-y: 0.5rem; + } + + .g-md-3, + .gx-md-3 { + --bs-gutter-x: 1rem; + } + + .g-md-3, + .gy-md-3 { + --bs-gutter-y: 1rem; + } + + .g-md-4, + .gx-md-4 { + --bs-gutter-x: 1.5rem; + } + + .g-md-4, + .gy-md-4 { + --bs-gutter-y: 1.5rem; + } + + .g-md-5, + .gx-md-5 { + --bs-gutter-x: 3rem; + } + + .g-md-5, + .gy-md-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 992px) { - .col-lg { - flex: 1 0 0%; - } - .row-cols-lg-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-lg-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-lg-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-lg-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-lg-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-lg-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-lg-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-auto { - flex: 0 0 auto; - width: auto; - } - .col-lg-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-lg-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-3 { - flex: 0 0 auto; - width: 25%; - } - .col-lg-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-lg-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-lg-6 { - flex: 0 0 auto; - width: 50%; - } - .col-lg-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-lg-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-lg-9 { - flex: 0 0 auto; - width: 75%; - } - .col-lg-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-lg-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-lg-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-lg-0 { - margin-right: 0; - } - .offset-lg-1 { - margin-right: 8.33333333%; - } - .offset-lg-2 { - margin-right: 16.66666667%; - } - .offset-lg-3 { - margin-right: 25%; - } - .offset-lg-4 { - margin-right: 33.33333333%; - } - .offset-lg-5 { - margin-right: 41.66666667%; - } - .offset-lg-6 { - margin-right: 50%; - } - .offset-lg-7 { - margin-right: 58.33333333%; - } - .offset-lg-8 { - margin-right: 66.66666667%; - } - .offset-lg-9 { - margin-right: 75%; - } - .offset-lg-10 { - margin-right: 83.33333333%; - } - .offset-lg-11 { - margin-right: 91.66666667%; - } - .g-lg-0, - .gx-lg-0 { - --bs-gutter-x: 0; - } - .g-lg-0, - .gy-lg-0 { - --bs-gutter-y: 0; - } - .g-lg-1, - .gx-lg-1 { - --bs-gutter-x: 0.25rem; - } - .g-lg-1, - .gy-lg-1 { - --bs-gutter-y: 0.25rem; - } - .g-lg-2, - .gx-lg-2 { - --bs-gutter-x: 0.5rem; - } - .g-lg-2, - .gy-lg-2 { - --bs-gutter-y: 0.5rem; - } - .g-lg-3, - .gx-lg-3 { - --bs-gutter-x: 1rem; - } - .g-lg-3, - .gy-lg-3 { - --bs-gutter-y: 1rem; - } - .g-lg-4, - .gx-lg-4 { - --bs-gutter-x: 1.5rem; - } - .g-lg-4, - .gy-lg-4 { - --bs-gutter-y: 1.5rem; - } - .g-lg-5, - .gx-lg-5 { - --bs-gutter-x: 3rem; - } - .g-lg-5, - .gy-lg-5 { - --bs-gutter-y: 3rem; - } + .col-lg { + flex: 1 0 0%; + } + + .row-cols-lg-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-lg-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-lg-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-lg-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-lg-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-lg-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-lg-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + } + + .col-lg-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-lg-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-lg-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-lg-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-lg-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-lg-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-lg-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-lg-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-lg-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-lg-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-lg-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-lg-0 { + margin-right: 0; + } + + .offset-lg-1 { + margin-right: 8.33333333%; + } + + .offset-lg-2 { + margin-right: 16.66666667%; + } + + .offset-lg-3 { + margin-right: 25%; + } + + .offset-lg-4 { + margin-right: 33.33333333%; + } + + .offset-lg-5 { + margin-right: 41.66666667%; + } + + .offset-lg-6 { + margin-right: 50%; + } + + .offset-lg-7 { + margin-right: 58.33333333%; + } + + .offset-lg-8 { + margin-right: 66.66666667%; + } + + .offset-lg-9 { + margin-right: 75%; + } + + .offset-lg-10 { + margin-right: 83.33333333%; + } + + .offset-lg-11 { + margin-right: 91.66666667%; + } + + .g-lg-0, + .gx-lg-0 { + --bs-gutter-x: 0; + } + + .g-lg-0, + .gy-lg-0 { + --bs-gutter-y: 0; + } + + .g-lg-1, + .gx-lg-1 { + --bs-gutter-x: 0.25rem; + } + + .g-lg-1, + .gy-lg-1 { + --bs-gutter-y: 0.25rem; + } + + .g-lg-2, + .gx-lg-2 { + --bs-gutter-x: 0.5rem; + } + + .g-lg-2, + .gy-lg-2 { + --bs-gutter-y: 0.5rem; + } + + .g-lg-3, + .gx-lg-3 { + --bs-gutter-x: 1rem; + } + + .g-lg-3, + .gy-lg-3 { + --bs-gutter-y: 1rem; + } + + .g-lg-4, + .gx-lg-4 { + --bs-gutter-x: 1.5rem; + } + + .g-lg-4, + .gy-lg-4 { + --bs-gutter-y: 1.5rem; + } + + .g-lg-5, + .gx-lg-5 { + --bs-gutter-x: 3rem; + } + + .g-lg-5, + .gy-lg-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 1200px) { - .col-xl { - flex: 1 0 0%; - } - .row-cols-xl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xl-0 { - margin-right: 0; - } - .offset-xl-1 { - margin-right: 8.33333333%; - } - .offset-xl-2 { - margin-right: 16.66666667%; - } - .offset-xl-3 { - margin-right: 25%; - } - .offset-xl-4 { - margin-right: 33.33333333%; - } - .offset-xl-5 { - margin-right: 41.66666667%; - } - .offset-xl-6 { - margin-right: 50%; - } - .offset-xl-7 { - margin-right: 58.33333333%; - } - .offset-xl-8 { - margin-right: 66.66666667%; - } - .offset-xl-9 { - margin-right: 75%; - } - .offset-xl-10 { - margin-right: 83.33333333%; - } - .offset-xl-11 { - margin-right: 91.66666667%; - } - .g-xl-0, - .gx-xl-0 { - --bs-gutter-x: 0; - } - .g-xl-0, - .gy-xl-0 { - --bs-gutter-y: 0; - } - .g-xl-1, - .gx-xl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xl-1, - .gy-xl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xl-2, - .gx-xl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xl-2, - .gy-xl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xl-3, - .gx-xl-3 { - --bs-gutter-x: 1rem; - } - .g-xl-3, - .gy-xl-3 { - --bs-gutter-y: 1rem; - } - .g-xl-4, - .gx-xl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xl-4, - .gy-xl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xl-5, - .gx-xl-5 { - --bs-gutter-x: 3rem; - } - .g-xl-5, - .gy-xl-5 { - --bs-gutter-y: 3rem; - } + .col-xl { + flex: 1 0 0%; + } + + .row-cols-xl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xl-0 { + margin-right: 0; + } + + .offset-xl-1 { + margin-right: 8.33333333%; + } + + .offset-xl-2 { + margin-right: 16.66666667%; + } + + .offset-xl-3 { + margin-right: 25%; + } + + .offset-xl-4 { + margin-right: 33.33333333%; + } + + .offset-xl-5 { + margin-right: 41.66666667%; + } + + .offset-xl-6 { + margin-right: 50%; + } + + .offset-xl-7 { + margin-right: 58.33333333%; + } + + .offset-xl-8 { + margin-right: 66.66666667%; + } + + .offset-xl-9 { + margin-right: 75%; + } + + .offset-xl-10 { + margin-right: 83.33333333%; + } + + .offset-xl-11 { + margin-right: 91.66666667%; + } + + .g-xl-0, + .gx-xl-0 { + --bs-gutter-x: 0; + } + + .g-xl-0, + .gy-xl-0 { + --bs-gutter-y: 0; + } + + .g-xl-1, + .gx-xl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xl-1, + .gy-xl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xl-2, + .gx-xl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xl-2, + .gy-xl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xl-3, + .gx-xl-3 { + --bs-gutter-x: 1rem; + } + + .g-xl-3, + .gy-xl-3 { + --bs-gutter-y: 1rem; + } + + .g-xl-4, + .gx-xl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xl-4, + .gy-xl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xl-5, + .gx-xl-5 { + --bs-gutter-x: 3rem; + } + + .g-xl-5, + .gy-xl-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 1400px) { - .col-xxl { - flex: 1 0 0%; - } - .row-cols-xxl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xxl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xxl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xxl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xxl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xxl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xxl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xxl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xxl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xxl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xxl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xxl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xxl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xxl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xxl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xxl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xxl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xxl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xxl-0 { - margin-right: 0; - } - .offset-xxl-1 { - margin-right: 8.33333333%; - } - .offset-xxl-2 { - margin-right: 16.66666667%; - } - .offset-xxl-3 { - margin-right: 25%; - } - .offset-xxl-4 { - margin-right: 33.33333333%; - } - .offset-xxl-5 { - margin-right: 41.66666667%; - } - .offset-xxl-6 { - margin-right: 50%; - } - .offset-xxl-7 { - margin-right: 58.33333333%; - } - .offset-xxl-8 { - margin-right: 66.66666667%; - } - .offset-xxl-9 { - margin-right: 75%; - } - .offset-xxl-10 { - margin-right: 83.33333333%; - } - .offset-xxl-11 { - margin-right: 91.66666667%; - } - .g-xxl-0, - .gx-xxl-0 { - --bs-gutter-x: 0; - } - .g-xxl-0, - .gy-xxl-0 { - --bs-gutter-y: 0; - } - .g-xxl-1, - .gx-xxl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xxl-1, - .gy-xxl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xxl-2, - .gx-xxl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xxl-2, - .gy-xxl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xxl-3, - .gx-xxl-3 { - --bs-gutter-x: 1rem; - } - .g-xxl-3, - .gy-xxl-3 { - --bs-gutter-y: 1rem; - } - .g-xxl-4, - .gx-xxl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xxl-4, - .gy-xxl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xxl-5, - .gx-xxl-5 { - --bs-gutter-x: 3rem; - } - .g-xxl-5, - .gy-xxl-5 { - --bs-gutter-y: 3rem; - } + .col-xxl { + flex: 1 0 0%; + } + + .row-cols-xxl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xxl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xxl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xxl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xxl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xxl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xxl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xxl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xxl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xxl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xxl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xxl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xxl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xxl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xxl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xxl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xxl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xxl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xxl-0 { + margin-right: 0; + } + + .offset-xxl-1 { + margin-right: 8.33333333%; + } + + .offset-xxl-2 { + margin-right: 16.66666667%; + } + + .offset-xxl-3 { + margin-right: 25%; + } + + .offset-xxl-4 { + margin-right: 33.33333333%; + } + + .offset-xxl-5 { + margin-right: 41.66666667%; + } + + .offset-xxl-6 { + margin-right: 50%; + } + + .offset-xxl-7 { + margin-right: 58.33333333%; + } + + .offset-xxl-8 { + margin-right: 66.66666667%; + } + + .offset-xxl-9 { + margin-right: 75%; + } + + .offset-xxl-10 { + margin-right: 83.33333333%; + } + + .offset-xxl-11 { + margin-right: 91.66666667%; + } + + .g-xxl-0, + .gx-xxl-0 { + --bs-gutter-x: 0; + } + + .g-xxl-0, + .gy-xxl-0 { + --bs-gutter-y: 0; + } + + .g-xxl-1, + .gx-xxl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xxl-1, + .gy-xxl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xxl-2, + .gx-xxl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xxl-2, + .gy-xxl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xxl-3, + .gx-xxl-3 { + --bs-gutter-x: 1rem; + } + + .g-xxl-3, + .gy-xxl-3 { + --bs-gutter-y: 1rem; + } + + .g-xxl-4, + .gx-xxl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xxl-4, + .gy-xxl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xxl-5, + .gx-xxl-5 { + --bs-gutter-x: 3rem; + } + + .g-xxl-5, + .gy-xxl-5 { + --bs-gutter-y: 3rem; + } } + .d-inline { - display: inline !important; + display: inline !important; } .d-inline-block { - display: inline-block !important; + display: inline-block !important; } .d-block { - display: block !important; + display: block !important; } .d-grid { - display: grid !important; + display: grid !important; } .d-inline-grid { - display: inline-grid !important; + display: inline-grid !important; } .d-table { - display: table !important; + display: table !important; } .d-table-row { - display: table-row !important; + display: table-row !important; } .d-table-cell { - display: table-cell !important; + display: table-cell !important; } .d-flex { - display: flex !important; + display: flex !important; } .d-inline-flex { - display: inline-flex !important; + display: inline-flex !important; } .d-none { - display: none !important; + display: none !important; } .flex-fill { - flex: 1 1 auto !important; + flex: 1 1 auto !important; } .flex-row { - flex-direction: row !important; + flex-direction: row !important; } .flex-column { - flex-direction: column !important; + flex-direction: column !important; } .flex-row-reverse { - flex-direction: row-reverse !important; + flex-direction: row-reverse !important; } .flex-column-reverse { - flex-direction: column-reverse !important; + flex-direction: column-reverse !important; } .flex-grow-0 { - flex-grow: 0 !important; + flex-grow: 0 !important; } .flex-grow-1 { - flex-grow: 1 !important; + flex-grow: 1 !important; } .flex-shrink-0 { - flex-shrink: 0 !important; + flex-shrink: 0 !important; } .flex-shrink-1 { - flex-shrink: 1 !important; + flex-shrink: 1 !important; } .flex-wrap { - flex-wrap: wrap !important; + flex-wrap: wrap !important; } .flex-nowrap { - flex-wrap: nowrap !important; + flex-wrap: nowrap !important; } .flex-wrap-reverse { - flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; } .justify-content-start { - justify-content: flex-start !important; + justify-content: flex-start !important; } .justify-content-end { - justify-content: flex-end !important; + justify-content: flex-end !important; } .justify-content-center { - justify-content: center !important; + justify-content: center !important; } .justify-content-between { - justify-content: space-between !important; + justify-content: space-between !important; } .justify-content-around { - justify-content: space-around !important; + justify-content: space-around !important; } .justify-content-evenly { - justify-content: space-evenly !important; + justify-content: space-evenly !important; } .align-items-start { - align-items: flex-start !important; + align-items: flex-start !important; } .align-items-end { - align-items: flex-end !important; + align-items: flex-end !important; } .align-items-center { - align-items: center !important; + align-items: center !important; } .align-items-baseline { - align-items: baseline !important; + align-items: baseline !important; } .align-items-stretch { - align-items: stretch !important; + align-items: stretch !important; } .align-content-start { - align-content: flex-start !important; + align-content: flex-start !important; } .align-content-end { - align-content: flex-end !important; + align-content: flex-end !important; } .align-content-center { - align-content: center !important; + align-content: center !important; } .align-content-between { - align-content: space-between !important; + align-content: space-between !important; } .align-content-around { - align-content: space-around !important; + align-content: space-around !important; } .align-content-stretch { - align-content: stretch !important; + align-content: stretch !important; } .align-self-auto { - align-self: auto !important; + align-self: auto !important; } .align-self-start { - align-self: flex-start !important; + align-self: flex-start !important; } .align-self-end { - align-self: flex-end !important; + align-self: flex-end !important; } .align-self-center { - align-self: center !important; + align-self: center !important; } .align-self-baseline { - align-self: baseline !important; + align-self: baseline !important; } .align-self-stretch { - align-self: stretch !important; + align-self: stretch !important; } .order-first { - order: -1 !important; + order: -1 !important; } .order-0 { - order: 0 !important; + order: 0 !important; } .order-1 { - order: 1 !important; + order: 1 !important; } .order-2 { - order: 2 !important; + order: 2 !important; } .order-3 { - order: 3 !important; + order: 3 !important; } .order-4 { - order: 4 !important; + order: 4 !important; } .order-5 { - order: 5 !important; + order: 5 !important; } .order-last { - order: 6 !important; + order: 6 !important; } .m-0 { - margin: 0 !important; + margin: 0 !important; } .m-1 { - margin: 0.25rem !important; + margin: 0.25rem !important; } .m-2 { - margin: 0.5rem !important; + margin: 0.5rem !important; } .m-3 { - margin: 1rem !important; + margin: 1rem !important; } .m-4 { - margin: 1.5rem !important; + margin: 1.5rem !important; } .m-5 { - margin: 3rem !important; + margin: 3rem !important; } .m-auto { - margin: auto !important; + margin: auto !important; } .mx-0 { - margin-left: 0 !important; - margin-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; } .mx-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; } .mx-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; } .mx-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; + margin-left: 1rem !important; + margin-right: 1rem !important; } .mx-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; } .mx-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; + margin-left: 3rem !important; + margin-right: 3rem !important; } .mx-auto { - margin-left: auto !important; - margin-right: auto !important; + margin-left: auto !important; + margin-right: auto !important; } .my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; } .my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } .my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } .my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; + margin-top: 1rem !important; + margin-bottom: 1rem !important; } .my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } .my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; + margin-top: 3rem !important; + margin-bottom: 3rem !important; } .my-auto { - margin-top: auto !important; - margin-bottom: auto !important; + margin-top: auto !important; + margin-bottom: auto !important; } .mt-0 { - margin-top: 0 !important; + margin-top: 0 !important; } .mt-1 { - margin-top: 0.25rem !important; + margin-top: 0.25rem !important; } .mt-2 { - margin-top: 0.5rem !important; + margin-top: 0.5rem !important; } .mt-3 { - margin-top: 1rem !important; + margin-top: 1rem !important; } .mt-4 { - margin-top: 1.5rem !important; + margin-top: 1.5rem !important; } .mt-5 { - margin-top: 3rem !important; + margin-top: 3rem !important; } .mt-auto { - margin-top: auto !important; + margin-top: auto !important; } .me-0 { - margin-left: 0 !important; + margin-left: 0 !important; } .me-1 { - margin-left: 0.25rem !important; + margin-left: 0.25rem !important; } .me-2 { - margin-left: 0.5rem !important; + margin-left: 0.5rem !important; } .me-3 { - margin-left: 1rem !important; + margin-left: 1rem !important; } .me-4 { - margin-left: 1.5rem !important; + margin-left: 1.5rem !important; } .me-5 { - margin-left: 3rem !important; + margin-left: 3rem !important; } .me-auto { - margin-left: auto !important; + margin-left: auto !important; } .mb-0 { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .mb-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 0.25rem !important; } .mb-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 0.5rem !important; } .mb-3 { - margin-bottom: 1rem !important; + margin-bottom: 1rem !important; } .mb-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 1.5rem !important; } .mb-5 { - margin-bottom: 3rem !important; + margin-bottom: 3rem !important; } .mb-auto { - margin-bottom: auto !important; + margin-bottom: auto !important; } .ms-0 { - margin-right: 0 !important; + margin-right: 0 !important; } .ms-1 { - margin-right: 0.25rem !important; + margin-right: 0.25rem !important; } .ms-2 { - margin-right: 0.5rem !important; + margin-right: 0.5rem !important; } .ms-3 { - margin-right: 1rem !important; + margin-right: 1rem !important; } .ms-4 { - margin-right: 1.5rem !important; + margin-right: 1.5rem !important; } .ms-5 { - margin-right: 3rem !important; + margin-right: 3rem !important; } .ms-auto { - margin-right: auto !important; + margin-right: auto !important; } .p-0 { - padding: 0 !important; + padding: 0 !important; } .p-1 { - padding: 0.25rem !important; + padding: 0.25rem !important; } .p-2 { - padding: 0.5rem !important; + padding: 0.5rem !important; } .p-3 { - padding: 1rem !important; + padding: 1rem !important; } .p-4 { - padding: 1.5rem !important; + padding: 1.5rem !important; } .p-5 { - padding: 3rem !important; + padding: 3rem !important; } .px-0 { - padding-left: 0 !important; - padding-right: 0 !important; + padding-left: 0 !important; + padding-right: 0 !important; } .px-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; } .px-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; } .px-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; + padding-left: 1rem !important; + padding-right: 1rem !important; } .px-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; } .px-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; + padding-left: 3rem !important; + padding-right: 3rem !important; } .py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; } .py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } .py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } .py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; + padding-top: 1rem !important; + padding-bottom: 1rem !important; } .py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } .py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; + padding-top: 3rem !important; + padding-bottom: 3rem !important; } .pt-0 { - padding-top: 0 !important; + padding-top: 0 !important; } .pt-1 { - padding-top: 0.25rem !important; + padding-top: 0.25rem !important; } .pt-2 { - padding-top: 0.5rem !important; + padding-top: 0.5rem !important; } .pt-3 { - padding-top: 1rem !important; + padding-top: 1rem !important; } .pt-4 { - padding-top: 1.5rem !important; + padding-top: 1.5rem !important; } .pt-5 { - padding-top: 3rem !important; + padding-top: 3rem !important; } .pe-0 { - padding-left: 0 !important; + padding-left: 0 !important; } .pe-1 { - padding-left: 0.25rem !important; + padding-left: 0.25rem !important; } .pe-2 { - padding-left: 0.5rem !important; + padding-left: 0.5rem !important; } .pe-3 { - padding-left: 1rem !important; + padding-left: 1rem !important; } .pe-4 { - padding-left: 1.5rem !important; + padding-left: 1.5rem !important; } .pe-5 { - padding-left: 3rem !important; + padding-left: 3rem !important; } .pb-0 { - padding-bottom: 0 !important; + padding-bottom: 0 !important; } .pb-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 0.25rem !important; } .pb-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 0.5rem !important; } .pb-3 { - padding-bottom: 1rem !important; + padding-bottom: 1rem !important; } .pb-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 1.5rem !important; } .pb-5 { - padding-bottom: 3rem !important; + padding-bottom: 3rem !important; } .ps-0 { - padding-right: 0 !important; + padding-right: 0 !important; } .ps-1 { - padding-right: 0.25rem !important; + padding-right: 0.25rem !important; } .ps-2 { - padding-right: 0.5rem !important; + padding-right: 0.5rem !important; } .ps-3 { - padding-right: 1rem !important; + padding-right: 1rem !important; } .ps-4 { - padding-right: 1.5rem !important; + padding-right: 1.5rem !important; } .ps-5 { - padding-right: 3rem !important; + padding-right: 3rem !important; } @media (min-width: 576px) { - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-sm-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-sm-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-sm-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-sm-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-sm-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-sm-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-left: 0 !important; - } - .me-sm-1 { - margin-left: 0.25rem !important; - } - .me-sm-2 { - margin-left: 0.5rem !important; - } - .me-sm-3 { - margin-left: 1rem !important; - } - .me-sm-4 { - margin-left: 1.5rem !important; - } - .me-sm-5 { - margin-left: 3rem !important; - } - .me-sm-auto { - margin-left: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-right: 0 !important; - } - .ms-sm-1 { - margin-right: 0.25rem !important; - } - .ms-sm-2 { - margin-right: 0.5rem !important; - } - .ms-sm-3 { - margin-right: 1rem !important; - } - .ms-sm-4 { - margin-right: 1.5rem !important; - } - .ms-sm-5 { - margin-right: 3rem !important; - } - .ms-sm-auto { - margin-right: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-sm-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-sm-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-sm-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-sm-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-sm-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-left: 0 !important; - } - .pe-sm-1 { - padding-left: 0.25rem !important; - } - .pe-sm-2 { - padding-left: 0.5rem !important; - } - .pe-sm-3 { - padding-left: 1rem !important; - } - .pe-sm-4 { - padding-left: 1.5rem !important; - } - .pe-sm-5 { - padding-left: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-right: 0 !important; - } - .ps-sm-1 { - padding-right: 0.25rem !important; - } - .ps-sm-2 { - padding-right: 0.5rem !important; - } - .ps-sm-3 { - padding-right: 1rem !important; - } - .ps-sm-4 { - padding-right: 1.5rem !important; - } - .ps-sm-5 { - padding-right: 3rem !important; - } -} -@media (min-width: 768px) { - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-md-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-md-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-md-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-md-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-md-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-md-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-left: 0 !important; - } - .me-md-1 { - margin-left: 0.25rem !important; - } - .me-md-2 { - margin-left: 0.5rem !important; - } - .me-md-3 { - margin-left: 1rem !important; - } - .me-md-4 { - margin-left: 1.5rem !important; - } - .me-md-5 { - margin-left: 3rem !important; - } - .me-md-auto { - margin-left: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-right: 0 !important; - } - .ms-md-1 { - margin-right: 0.25rem !important; - } - .ms-md-2 { - margin-right: 0.5rem !important; - } - .ms-md-3 { - margin-right: 1rem !important; - } - .ms-md-4 { - margin-right: 1.5rem !important; - } - .ms-md-5 { - margin-right: 3rem !important; - } - .ms-md-auto { - margin-right: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-md-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-md-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-md-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-md-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-md-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-left: 0 !important; - } - .pe-md-1 { - padding-left: 0.25rem !important; - } - .pe-md-2 { - padding-left: 0.5rem !important; - } - .pe-md-3 { - padding-left: 1rem !important; - } - .pe-md-4 { - padding-left: 1.5rem !important; - } - .pe-md-5 { - padding-left: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-right: 0 !important; - } - .ps-md-1 { - padding-right: 0.25rem !important; - } - .ps-md-2 { - padding-right: 0.5rem !important; - } - .ps-md-3 { - padding-right: 1rem !important; - } - .ps-md-4 { - padding-right: 1.5rem !important; - } - .ps-md-5 { - padding-right: 3rem !important; - } + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-grid { + display: grid !important; + } + + .d-sm-inline-grid { + display: inline-grid !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: flex !important; + } + + .d-sm-inline-flex { + display: inline-flex !important; + } + + .d-sm-none { + display: none !important; + } + + .flex-sm-fill { + flex: 1 1 auto !important; + } + + .flex-sm-row { + flex-direction: row !important; + } + + .flex-sm-column { + flex-direction: column !important; + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-sm-wrap { + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-sm-start { + justify-content: flex-start !important; + } + + .justify-content-sm-end { + justify-content: flex-end !important; + } + + .justify-content-sm-center { + justify-content: center !important; + } + + .justify-content-sm-between { + justify-content: space-between !important; + } + + .justify-content-sm-around { + justify-content: space-around !important; + } + + .justify-content-sm-evenly { + justify-content: space-evenly !important; + } + + .align-items-sm-start { + align-items: flex-start !important; + } + + .align-items-sm-end { + align-items: flex-end !important; + } + + .align-items-sm-center { + align-items: center !important; + } + + .align-items-sm-baseline { + align-items: baseline !important; + } + + .align-items-sm-stretch { + align-items: stretch !important; + } + + .align-content-sm-start { + align-content: flex-start !important; + } + + .align-content-sm-end { + align-content: flex-end !important; + } + + .align-content-sm-center { + align-content: center !important; + } + + .align-content-sm-between { + align-content: space-between !important; + } + + .align-content-sm-around { + align-content: space-around !important; + } + + .align-content-sm-stretch { + align-content: stretch !important; + } + + .align-self-sm-auto { + align-self: auto !important; + } + + .align-self-sm-start { + align-self: flex-start !important; + } + + .align-self-sm-end { + align-self: flex-end !important; + } + + .align-self-sm-center { + align-self: center !important; + } + + .align-self-sm-baseline { + align-self: baseline !important; + } + + .align-self-sm-stretch { + align-self: stretch !important; + } + + .order-sm-first { + order: -1 !important; + } + + .order-sm-0 { + order: 0 !important; + } + + .order-sm-1 { + order: 1 !important; + } + + .order-sm-2 { + order: 2 !important; + } + + .order-sm-3 { + order: 3 !important; + } + + .order-sm-4 { + order: 4 !important; + } + + .order-sm-5 { + order: 5 !important; + } + + .order-sm-last { + order: 6 !important; + } + + .m-sm-0 { + margin: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mx-sm-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-sm-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-sm-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-sm-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-sm-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-sm-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-sm-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-sm-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-sm-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-sm-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-sm-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-sm-0 { + margin-top: 0 !important; + } + + .mt-sm-1 { + margin-top: 0.25rem !important; + } + + .mt-sm-2 { + margin-top: 0.5rem !important; + } + + .mt-sm-3 { + margin-top: 1rem !important; + } + + .mt-sm-4 { + margin-top: 1.5rem !important; + } + + .mt-sm-5 { + margin-top: 3rem !important; + } + + .mt-sm-auto { + margin-top: auto !important; + } + + .me-sm-0 { + margin-left: 0 !important; + } + + .me-sm-1 { + margin-left: 0.25rem !important; + } + + .me-sm-2 { + margin-left: 0.5rem !important; + } + + .me-sm-3 { + margin-left: 1rem !important; + } + + .me-sm-4 { + margin-left: 1.5rem !important; + } + + .me-sm-5 { + margin-left: 3rem !important; + } + + .me-sm-auto { + margin-left: auto !important; + } + + .mb-sm-0 { + margin-bottom: 0 !important; + } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; + } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; + } + + .mb-sm-3 { + margin-bottom: 1rem !important; + } + + .mb-sm-4 { + margin-bottom: 1.5rem !important; + } + + .mb-sm-5 { + margin-bottom: 3rem !important; + } + + .mb-sm-auto { + margin-bottom: auto !important; + } + + .ms-sm-0 { + margin-right: 0 !important; + } + + .ms-sm-1 { + margin-right: 0.25rem !important; + } + + .ms-sm-2 { + margin-right: 0.5rem !important; + } + + .ms-sm-3 { + margin-right: 1rem !important; + } + + .ms-sm-4 { + margin-right: 1.5rem !important; + } + + .ms-sm-5 { + margin-right: 3rem !important; + } + + .ms-sm-auto { + margin-right: auto !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .px-sm-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-sm-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-sm-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-sm-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-sm-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-sm-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-sm-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-sm-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-sm-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-sm-0 { + padding-top: 0 !important; + } + + .pt-sm-1 { + padding-top: 0.25rem !important; + } + + .pt-sm-2 { + padding-top: 0.5rem !important; + } + + .pt-sm-3 { + padding-top: 1rem !important; + } + + .pt-sm-4 { + padding-top: 1.5rem !important; + } + + .pt-sm-5 { + padding-top: 3rem !important; + } + + .pe-sm-0 { + padding-left: 0 !important; + } + + .pe-sm-1 { + padding-left: 0.25rem !important; + } + + .pe-sm-2 { + padding-left: 0.5rem !important; + } + + .pe-sm-3 { + padding-left: 1rem !important; + } + + .pe-sm-4 { + padding-left: 1.5rem !important; + } + + .pe-sm-5 { + padding-left: 3rem !important; + } + + .pb-sm-0 { + padding-bottom: 0 !important; + } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pb-sm-3 { + padding-bottom: 1rem !important; + } + + .pb-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pb-sm-5 { + padding-bottom: 3rem !important; + } + + .ps-sm-0 { + padding-right: 0 !important; + } + + .ps-sm-1 { + padding-right: 0.25rem !important; + } + + .ps-sm-2 { + padding-right: 0.5rem !important; + } + + .ps-sm-3 { + padding-right: 1rem !important; + } + + .ps-sm-4 { + padding-right: 1.5rem !important; + } + + .ps-sm-5 { + padding-right: 3rem !important; + } +} + +@media (min-width: 768px) { + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-grid { + display: grid !important; + } + + .d-md-inline-grid { + display: inline-grid !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: flex !important; + } + + .d-md-inline-flex { + display: inline-flex !important; + } + + .d-md-none { + display: none !important; + } + + .flex-md-fill { + flex: 1 1 auto !important; + } + + .flex-md-row { + flex-direction: row !important; + } + + .flex-md-column { + flex-direction: column !important; + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-md-grow-0 { + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-md-wrap { + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-md-start { + justify-content: flex-start !important; + } + + .justify-content-md-end { + justify-content: flex-end !important; + } + + .justify-content-md-center { + justify-content: center !important; + } + + .justify-content-md-between { + justify-content: space-between !important; + } + + .justify-content-md-around { + justify-content: space-around !important; + } + + .justify-content-md-evenly { + justify-content: space-evenly !important; + } + + .align-items-md-start { + align-items: flex-start !important; + } + + .align-items-md-end { + align-items: flex-end !important; + } + + .align-items-md-center { + align-items: center !important; + } + + .align-items-md-baseline { + align-items: baseline !important; + } + + .align-items-md-stretch { + align-items: stretch !important; + } + + .align-content-md-start { + align-content: flex-start !important; + } + + .align-content-md-end { + align-content: flex-end !important; + } + + .align-content-md-center { + align-content: center !important; + } + + .align-content-md-between { + align-content: space-between !important; + } + + .align-content-md-around { + align-content: space-around !important; + } + + .align-content-md-stretch { + align-content: stretch !important; + } + + .align-self-md-auto { + align-self: auto !important; + } + + .align-self-md-start { + align-self: flex-start !important; + } + + .align-self-md-end { + align-self: flex-end !important; + } + + .align-self-md-center { + align-self: center !important; + } + + .align-self-md-baseline { + align-self: baseline !important; + } + + .align-self-md-stretch { + align-self: stretch !important; + } + + .order-md-first { + order: -1 !important; + } + + .order-md-0 { + order: 0 !important; + } + + .order-md-1 { + order: 1 !important; + } + + .order-md-2 { + order: 2 !important; + } + + .order-md-3 { + order: 3 !important; + } + + .order-md-4 { + order: 4 !important; + } + + .order-md-5 { + order: 5 !important; + } + + .order-md-last { + order: 6 !important; + } + + .m-md-0 { + margin: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mx-md-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-md-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-md-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-md-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-md-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-md-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-md-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-md-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-md-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-md-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-md-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-md-0 { + margin-top: 0 !important; + } + + .mt-md-1 { + margin-top: 0.25rem !important; + } + + .mt-md-2 { + margin-top: 0.5rem !important; + } + + .mt-md-3 { + margin-top: 1rem !important; + } + + .mt-md-4 { + margin-top: 1.5rem !important; + } + + .mt-md-5 { + margin-top: 3rem !important; + } + + .mt-md-auto { + margin-top: auto !important; + } + + .me-md-0 { + margin-left: 0 !important; + } + + .me-md-1 { + margin-left: 0.25rem !important; + } + + .me-md-2 { + margin-left: 0.5rem !important; + } + + .me-md-3 { + margin-left: 1rem !important; + } + + .me-md-4 { + margin-left: 1.5rem !important; + } + + .me-md-5 { + margin-left: 3rem !important; + } + + .me-md-auto { + margin-left: auto !important; + } + + .mb-md-0 { + margin-bottom: 0 !important; + } + + .mb-md-1 { + margin-bottom: 0.25rem !important; + } + + .mb-md-2 { + margin-bottom: 0.5rem !important; + } + + .mb-md-3 { + margin-bottom: 1rem !important; + } + + .mb-md-4 { + margin-bottom: 1.5rem !important; + } + + .mb-md-5 { + margin-bottom: 3rem !important; + } + + .mb-md-auto { + margin-bottom: auto !important; + } + + .ms-md-0 { + margin-right: 0 !important; + } + + .ms-md-1 { + margin-right: 0.25rem !important; + } + + .ms-md-2 { + margin-right: 0.5rem !important; + } + + .ms-md-3 { + margin-right: 1rem !important; + } + + .ms-md-4 { + margin-right: 1.5rem !important; + } + + .ms-md-5 { + margin-right: 3rem !important; + } + + .ms-md-auto { + margin-right: auto !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .px-md-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-md-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-md-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-md-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-md-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-md-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-md-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-md-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-md-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-md-0 { + padding-top: 0 !important; + } + + .pt-md-1 { + padding-top: 0.25rem !important; + } + + .pt-md-2 { + padding-top: 0.5rem !important; + } + + .pt-md-3 { + padding-top: 1rem !important; + } + + .pt-md-4 { + padding-top: 1.5rem !important; + } + + .pt-md-5 { + padding-top: 3rem !important; + } + + .pe-md-0 { + padding-left: 0 !important; + } + + .pe-md-1 { + padding-left: 0.25rem !important; + } + + .pe-md-2 { + padding-left: 0.5rem !important; + } + + .pe-md-3 { + padding-left: 1rem !important; + } + + .pe-md-4 { + padding-left: 1.5rem !important; + } + + .pe-md-5 { + padding-left: 3rem !important; + } + + .pb-md-0 { + padding-bottom: 0 !important; + } + + .pb-md-1 { + padding-bottom: 0.25rem !important; + } + + .pb-md-2 { + padding-bottom: 0.5rem !important; + } + + .pb-md-3 { + padding-bottom: 1rem !important; + } + + .pb-md-4 { + padding-bottom: 1.5rem !important; + } + + .pb-md-5 { + padding-bottom: 3rem !important; + } + + .ps-md-0 { + padding-right: 0 !important; + } + + .ps-md-1 { + padding-right: 0.25rem !important; + } + + .ps-md-2 { + padding-right: 0.5rem !important; + } + + .ps-md-3 { + padding-right: 1rem !important; + } + + .ps-md-4 { + padding-right: 1.5rem !important; + } + + .ps-md-5 { + padding-right: 3rem !important; + } } + @media (min-width: 992px) { - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-lg-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-lg-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-lg-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-lg-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-lg-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-lg-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-left: 0 !important; - } - .me-lg-1 { - margin-left: 0.25rem !important; - } - .me-lg-2 { - margin-left: 0.5rem !important; - } - .me-lg-3 { - margin-left: 1rem !important; - } - .me-lg-4 { - margin-left: 1.5rem !important; - } - .me-lg-5 { - margin-left: 3rem !important; - } - .me-lg-auto { - margin-left: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-right: 0 !important; - } - .ms-lg-1 { - margin-right: 0.25rem !important; - } - .ms-lg-2 { - margin-right: 0.5rem !important; - } - .ms-lg-3 { - margin-right: 1rem !important; - } - .ms-lg-4 { - margin-right: 1.5rem !important; - } - .ms-lg-5 { - margin-right: 3rem !important; - } - .ms-lg-auto { - margin-right: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-lg-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-lg-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-lg-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-lg-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-lg-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-left: 0 !important; - } - .pe-lg-1 { - padding-left: 0.25rem !important; - } - .pe-lg-2 { - padding-left: 0.5rem !important; - } - .pe-lg-3 { - padding-left: 1rem !important; - } - .pe-lg-4 { - padding-left: 1.5rem !important; - } - .pe-lg-5 { - padding-left: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-right: 0 !important; - } - .ps-lg-1 { - padding-right: 0.25rem !important; - } - .ps-lg-2 { - padding-right: 0.5rem !important; - } - .ps-lg-3 { - padding-right: 1rem !important; - } - .ps-lg-4 { - padding-right: 1.5rem !important; - } - .ps-lg-5 { - padding-right: 3rem !important; - } + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-grid { + display: grid !important; + } + + .d-lg-inline-grid { + display: inline-grid !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: flex !important; + } + + .d-lg-inline-flex { + display: inline-flex !important; + } + + .d-lg-none { + display: none !important; + } + + .flex-lg-fill { + flex: 1 1 auto !important; + } + + .flex-lg-row { + flex-direction: row !important; + } + + .flex-lg-column { + flex-direction: column !important; + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-lg-wrap { + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-lg-start { + justify-content: flex-start !important; + } + + .justify-content-lg-end { + justify-content: flex-end !important; + } + + .justify-content-lg-center { + justify-content: center !important; + } + + .justify-content-lg-between { + justify-content: space-between !important; + } + + .justify-content-lg-around { + justify-content: space-around !important; + } + + .justify-content-lg-evenly { + justify-content: space-evenly !important; + } + + .align-items-lg-start { + align-items: flex-start !important; + } + + .align-items-lg-end { + align-items: flex-end !important; + } + + .align-items-lg-center { + align-items: center !important; + } + + .align-items-lg-baseline { + align-items: baseline !important; + } + + .align-items-lg-stretch { + align-items: stretch !important; + } + + .align-content-lg-start { + align-content: flex-start !important; + } + + .align-content-lg-end { + align-content: flex-end !important; + } + + .align-content-lg-center { + align-content: center !important; + } + + .align-content-lg-between { + align-content: space-between !important; + } + + .align-content-lg-around { + align-content: space-around !important; + } + + .align-content-lg-stretch { + align-content: stretch !important; + } + + .align-self-lg-auto { + align-self: auto !important; + } + + .align-self-lg-start { + align-self: flex-start !important; + } + + .align-self-lg-end { + align-self: flex-end !important; + } + + .align-self-lg-center { + align-self: center !important; + } + + .align-self-lg-baseline { + align-self: baseline !important; + } + + .align-self-lg-stretch { + align-self: stretch !important; + } + + .order-lg-first { + order: -1 !important; + } + + .order-lg-0 { + order: 0 !important; + } + + .order-lg-1 { + order: 1 !important; + } + + .order-lg-2 { + order: 2 !important; + } + + .order-lg-3 { + order: 3 !important; + } + + .order-lg-4 { + order: 4 !important; + } + + .order-lg-5 { + order: 5 !important; + } + + .order-lg-last { + order: 6 !important; + } + + .m-lg-0 { + margin: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mx-lg-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-lg-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-lg-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-lg-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-lg-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-lg-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-lg-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-lg-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-lg-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-lg-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-lg-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-lg-0 { + margin-top: 0 !important; + } + + .mt-lg-1 { + margin-top: 0.25rem !important; + } + + .mt-lg-2 { + margin-top: 0.5rem !important; + } + + .mt-lg-3 { + margin-top: 1rem !important; + } + + .mt-lg-4 { + margin-top: 1.5rem !important; + } + + .mt-lg-5 { + margin-top: 3rem !important; + } + + .mt-lg-auto { + margin-top: auto !important; + } + + .me-lg-0 { + margin-left: 0 !important; + } + + .me-lg-1 { + margin-left: 0.25rem !important; + } + + .me-lg-2 { + margin-left: 0.5rem !important; + } + + .me-lg-3 { + margin-left: 1rem !important; + } + + .me-lg-4 { + margin-left: 1.5rem !important; + } + + .me-lg-5 { + margin-left: 3rem !important; + } + + .me-lg-auto { + margin-left: auto !important; + } + + .mb-lg-0 { + margin-bottom: 0 !important; + } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; + } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; + } + + .mb-lg-3 { + margin-bottom: 1rem !important; + } + + .mb-lg-4 { + margin-bottom: 1.5rem !important; + } + + .mb-lg-5 { + margin-bottom: 3rem !important; + } + + .mb-lg-auto { + margin-bottom: auto !important; + } + + .ms-lg-0 { + margin-right: 0 !important; + } + + .ms-lg-1 { + margin-right: 0.25rem !important; + } + + .ms-lg-2 { + margin-right: 0.5rem !important; + } + + .ms-lg-3 { + margin-right: 1rem !important; + } + + .ms-lg-4 { + margin-right: 1.5rem !important; + } + + .ms-lg-5 { + margin-right: 3rem !important; + } + + .ms-lg-auto { + margin-right: auto !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .px-lg-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-lg-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-lg-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-lg-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-lg-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-lg-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-lg-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-lg-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-lg-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-lg-0 { + padding-top: 0 !important; + } + + .pt-lg-1 { + padding-top: 0.25rem !important; + } + + .pt-lg-2 { + padding-top: 0.5rem !important; + } + + .pt-lg-3 { + padding-top: 1rem !important; + } + + .pt-lg-4 { + padding-top: 1.5rem !important; + } + + .pt-lg-5 { + padding-top: 3rem !important; + } + + .pe-lg-0 { + padding-left: 0 !important; + } + + .pe-lg-1 { + padding-left: 0.25rem !important; + } + + .pe-lg-2 { + padding-left: 0.5rem !important; + } + + .pe-lg-3 { + padding-left: 1rem !important; + } + + .pe-lg-4 { + padding-left: 1.5rem !important; + } + + .pe-lg-5 { + padding-left: 3rem !important; + } + + .pb-lg-0 { + padding-bottom: 0 !important; + } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pb-lg-3 { + padding-bottom: 1rem !important; + } + + .pb-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pb-lg-5 { + padding-bottom: 3rem !important; + } + + .ps-lg-0 { + padding-right: 0 !important; + } + + .ps-lg-1 { + padding-right: 0.25rem !important; + } + + .ps-lg-2 { + padding-right: 0.5rem !important; + } + + .ps-lg-3 { + padding-right: 1rem !important; + } + + .ps-lg-4 { + padding-right: 1.5rem !important; + } + + .ps-lg-5 { + padding-right: 3rem !important; + } } + @media (min-width: 1200px) { - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-left: 0 !important; - } - .me-xl-1 { - margin-left: 0.25rem !important; - } - .me-xl-2 { - margin-left: 0.5rem !important; - } - .me-xl-3 { - margin-left: 1rem !important; - } - .me-xl-4 { - margin-left: 1.5rem !important; - } - .me-xl-5 { - margin-left: 3rem !important; - } - .me-xl-auto { - margin-left: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-right: 0 !important; - } - .ms-xl-1 { - margin-right: 0.25rem !important; - } - .ms-xl-2 { - margin-right: 0.5rem !important; - } - .ms-xl-3 { - margin-right: 1rem !important; - } - .ms-xl-4 { - margin-right: 1.5rem !important; - } - .ms-xl-5 { - margin-right: 3rem !important; - } - .ms-xl-auto { - margin-right: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-left: 0 !important; - } - .pe-xl-1 { - padding-left: 0.25rem !important; - } - .pe-xl-2 { - padding-left: 0.5rem !important; - } - .pe-xl-3 { - padding-left: 1rem !important; - } - .pe-xl-4 { - padding-left: 1.5rem !important; - } - .pe-xl-5 { - padding-left: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-right: 0 !important; - } - .ps-xl-1 { - padding-right: 0.25rem !important; - } - .ps-xl-2 { - padding-right: 0.5rem !important; - } - .ps-xl-3 { - padding-right: 1rem !important; - } - .ps-xl-4 { - padding-right: 1.5rem !important; - } - .ps-xl-5 { - padding-right: 3rem !important; - } + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-grid { + display: grid !important; + } + + .d-xl-inline-grid { + display: inline-grid !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: flex !important; + } + + .d-xl-inline-flex { + display: inline-flex !important; + } + + .d-xl-none { + display: none !important; + } + + .flex-xl-fill { + flex: 1 1 auto !important; + } + + .flex-xl-row { + flex-direction: row !important; + } + + .flex-xl-column { + flex-direction: column !important; + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xl-wrap { + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xl-start { + justify-content: flex-start !important; + } + + .justify-content-xl-end { + justify-content: flex-end !important; + } + + .justify-content-xl-center { + justify-content: center !important; + } + + .justify-content-xl-between { + justify-content: space-between !important; + } + + .justify-content-xl-around { + justify-content: space-around !important; + } + + .justify-content-xl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xl-start { + align-items: flex-start !important; + } + + .align-items-xl-end { + align-items: flex-end !important; + } + + .align-items-xl-center { + align-items: center !important; + } + + .align-items-xl-baseline { + align-items: baseline !important; + } + + .align-items-xl-stretch { + align-items: stretch !important; + } + + .align-content-xl-start { + align-content: flex-start !important; + } + + .align-content-xl-end { + align-content: flex-end !important; + } + + .align-content-xl-center { + align-content: center !important; + } + + .align-content-xl-between { + align-content: space-between !important; + } + + .align-content-xl-around { + align-content: space-around !important; + } + + .align-content-xl-stretch { + align-content: stretch !important; + } + + .align-self-xl-auto { + align-self: auto !important; + } + + .align-self-xl-start { + align-self: flex-start !important; + } + + .align-self-xl-end { + align-self: flex-end !important; + } + + .align-self-xl-center { + align-self: center !important; + } + + .align-self-xl-baseline { + align-self: baseline !important; + } + + .align-self-xl-stretch { + align-self: stretch !important; + } + + .order-xl-first { + order: -1 !important; + } + + .order-xl-0 { + order: 0 !important; + } + + .order-xl-1 { + order: 1 !important; + } + + .order-xl-2 { + order: 2 !important; + } + + .order-xl-3 { + order: 3 !important; + } + + .order-xl-4 { + order: 4 !important; + } + + .order-xl-5 { + order: 5 !important; + } + + .order-xl-last { + order: 6 !important; + } + + .m-xl-0 { + margin: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mx-xl-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-xl-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-xl-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-xl-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-xl-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-xl-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-xl-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xl-0 { + margin-top: 0 !important; + } + + .mt-xl-1 { + margin-top: 0.25rem !important; + } + + .mt-xl-2 { + margin-top: 0.5rem !important; + } + + .mt-xl-3 { + margin-top: 1rem !important; + } + + .mt-xl-4 { + margin-top: 1.5rem !important; + } + + .mt-xl-5 { + margin-top: 3rem !important; + } + + .mt-xl-auto { + margin-top: auto !important; + } + + .me-xl-0 { + margin-left: 0 !important; + } + + .me-xl-1 { + margin-left: 0.25rem !important; + } + + .me-xl-2 { + margin-left: 0.5rem !important; + } + + .me-xl-3 { + margin-left: 1rem !important; + } + + .me-xl-4 { + margin-left: 1.5rem !important; + } + + .me-xl-5 { + margin-left: 3rem !important; + } + + .me-xl-auto { + margin-left: auto !important; + } + + .mb-xl-0 { + margin-bottom: 0 !important; + } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xl-3 { + margin-bottom: 1rem !important; + } + + .mb-xl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xl-5 { + margin-bottom: 3rem !important; + } + + .mb-xl-auto { + margin-bottom: auto !important; + } + + .ms-xl-0 { + margin-right: 0 !important; + } + + .ms-xl-1 { + margin-right: 0.25rem !important; + } + + .ms-xl-2 { + margin-right: 0.5rem !important; + } + + .ms-xl-3 { + margin-right: 1rem !important; + } + + .ms-xl-4 { + margin-right: 1.5rem !important; + } + + .ms-xl-5 { + margin-right: 3rem !important; + } + + .ms-xl-auto { + margin-right: auto !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .px-xl-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-xl-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-xl-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-xl-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-xl-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-xl-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xl-0 { + padding-top: 0 !important; + } + + .pt-xl-1 { + padding-top: 0.25rem !important; + } + + .pt-xl-2 { + padding-top: 0.5rem !important; + } + + .pt-xl-3 { + padding-top: 1rem !important; + } + + .pt-xl-4 { + padding-top: 1.5rem !important; + } + + .pt-xl-5 { + padding-top: 3rem !important; + } + + .pe-xl-0 { + padding-left: 0 !important; + } + + .pe-xl-1 { + padding-left: 0.25rem !important; + } + + .pe-xl-2 { + padding-left: 0.5rem !important; + } + + .pe-xl-3 { + padding-left: 1rem !important; + } + + .pe-xl-4 { + padding-left: 1.5rem !important; + } + + .pe-xl-5 { + padding-left: 3rem !important; + } + + .pb-xl-0 { + padding-bottom: 0 !important; + } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xl-3 { + padding-bottom: 1rem !important; + } + + .pb-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xl-5 { + padding-bottom: 3rem !important; + } + + .ps-xl-0 { + padding-right: 0 !important; + } + + .ps-xl-1 { + padding-right: 0.25rem !important; + } + + .ps-xl-2 { + padding-right: 0.5rem !important; + } + + .ps-xl-3 { + padding-right: 1rem !important; + } + + .ps-xl-4 { + padding-right: 1.5rem !important; + } + + .ps-xl-5 { + padding-right: 3rem !important; + } } + @media (min-width: 1400px) { - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xxl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xxl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xxl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xxl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xxl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xxl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-left: 0 !important; - } - .me-xxl-1 { - margin-left: 0.25rem !important; - } - .me-xxl-2 { - margin-left: 0.5rem !important; - } - .me-xxl-3 { - margin-left: 1rem !important; - } - .me-xxl-4 { - margin-left: 1.5rem !important; - } - .me-xxl-5 { - margin-left: 3rem !important; - } - .me-xxl-auto { - margin-left: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-right: 0 !important; - } - .ms-xxl-1 { - margin-right: 0.25rem !important; - } - .ms-xxl-2 { - margin-right: 0.5rem !important; - } - .ms-xxl-3 { - margin-right: 1rem !important; - } - .ms-xxl-4 { - margin-right: 1.5rem !important; - } - .ms-xxl-5 { - margin-right: 3rem !important; - } - .ms-xxl-auto { - margin-right: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xxl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xxl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xxl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xxl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xxl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-left: 0 !important; - } - .pe-xxl-1 { - padding-left: 0.25rem !important; - } - .pe-xxl-2 { - padding-left: 0.5rem !important; - } - .pe-xxl-3 { - padding-left: 1rem !important; - } - .pe-xxl-4 { - padding-left: 1.5rem !important; - } - .pe-xxl-5 { - padding-left: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-right: 0 !important; - } - .ps-xxl-1 { - padding-right: 0.25rem !important; - } - .ps-xxl-2 { - padding-right: 0.5rem !important; - } - .ps-xxl-3 { - padding-right: 1rem !important; - } - .ps-xxl-4 { - padding-right: 1.5rem !important; - } - .ps-xxl-5 { - padding-right: 3rem !important; - } + .d-xxl-inline { + display: inline !important; + } + + .d-xxl-inline-block { + display: inline-block !important; + } + + .d-xxl-block { + display: block !important; + } + + .d-xxl-grid { + display: grid !important; + } + + .d-xxl-inline-grid { + display: inline-grid !important; + } + + .d-xxl-table { + display: table !important; + } + + .d-xxl-table-row { + display: table-row !important; + } + + .d-xxl-table-cell { + display: table-cell !important; + } + + .d-xxl-flex { + display: flex !important; + } + + .d-xxl-inline-flex { + display: inline-flex !important; + } + + .d-xxl-none { + display: none !important; + } + + .flex-xxl-fill { + flex: 1 1 auto !important; + } + + .flex-xxl-row { + flex-direction: row !important; + } + + .flex-xxl-column { + flex-direction: column !important; + } + + .flex-xxl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xxl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xxl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xxl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xxl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xxl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xxl-wrap { + flex-wrap: wrap !important; + } + + .flex-xxl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xxl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xxl-start { + justify-content: flex-start !important; + } + + .justify-content-xxl-end { + justify-content: flex-end !important; + } + + .justify-content-xxl-center { + justify-content: center !important; + } + + .justify-content-xxl-between { + justify-content: space-between !important; + } + + .justify-content-xxl-around { + justify-content: space-around !important; + } + + .justify-content-xxl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xxl-start { + align-items: flex-start !important; + } + + .align-items-xxl-end { + align-items: flex-end !important; + } + + .align-items-xxl-center { + align-items: center !important; + } + + .align-items-xxl-baseline { + align-items: baseline !important; + } + + .align-items-xxl-stretch { + align-items: stretch !important; + } + + .align-content-xxl-start { + align-content: flex-start !important; + } + + .align-content-xxl-end { + align-content: flex-end !important; + } + + .align-content-xxl-center { + align-content: center !important; + } + + .align-content-xxl-between { + align-content: space-between !important; + } + + .align-content-xxl-around { + align-content: space-around !important; + } + + .align-content-xxl-stretch { + align-content: stretch !important; + } + + .align-self-xxl-auto { + align-self: auto !important; + } + + .align-self-xxl-start { + align-self: flex-start !important; + } + + .align-self-xxl-end { + align-self: flex-end !important; + } + + .align-self-xxl-center { + align-self: center !important; + } + + .align-self-xxl-baseline { + align-self: baseline !important; + } + + .align-self-xxl-stretch { + align-self: stretch !important; + } + + .order-xxl-first { + order: -1 !important; + } + + .order-xxl-0 { + order: 0 !important; + } + + .order-xxl-1 { + order: 1 !important; + } + + .order-xxl-2 { + order: 2 !important; + } + + .order-xxl-3 { + order: 3 !important; + } + + .order-xxl-4 { + order: 4 !important; + } + + .order-xxl-5 { + order: 5 !important; + } + + .order-xxl-last { + order: 6 !important; + } + + .m-xxl-0 { + margin: 0 !important; + } + + .m-xxl-1 { + margin: 0.25rem !important; + } + + .m-xxl-2 { + margin: 0.5rem !important; + } + + .m-xxl-3 { + margin: 1rem !important; + } + + .m-xxl-4 { + margin: 1.5rem !important; + } + + .m-xxl-5 { + margin: 3rem !important; + } + + .m-xxl-auto { + margin: auto !important; + } + + .mx-xxl-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-xxl-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-xxl-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-xxl-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-xxl-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-xxl-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-xxl-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-xxl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xxl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xxl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xxl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xxl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xxl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xxl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xxl-0 { + margin-top: 0 !important; + } + + .mt-xxl-1 { + margin-top: 0.25rem !important; + } + + .mt-xxl-2 { + margin-top: 0.5rem !important; + } + + .mt-xxl-3 { + margin-top: 1rem !important; + } + + .mt-xxl-4 { + margin-top: 1.5rem !important; + } + + .mt-xxl-5 { + margin-top: 3rem !important; + } + + .mt-xxl-auto { + margin-top: auto !important; + } + + .me-xxl-0 { + margin-left: 0 !important; + } + + .me-xxl-1 { + margin-left: 0.25rem !important; + } + + .me-xxl-2 { + margin-left: 0.5rem !important; + } + + .me-xxl-3 { + margin-left: 1rem !important; + } + + .me-xxl-4 { + margin-left: 1.5rem !important; + } + + .me-xxl-5 { + margin-left: 3rem !important; + } + + .me-xxl-auto { + margin-left: auto !important; + } + + .mb-xxl-0 { + margin-bottom: 0 !important; + } + + .mb-xxl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xxl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xxl-3 { + margin-bottom: 1rem !important; + } + + .mb-xxl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xxl-5 { + margin-bottom: 3rem !important; + } + + .mb-xxl-auto { + margin-bottom: auto !important; + } + + .ms-xxl-0 { + margin-right: 0 !important; + } + + .ms-xxl-1 { + margin-right: 0.25rem !important; + } + + .ms-xxl-2 { + margin-right: 0.5rem !important; + } + + .ms-xxl-3 { + margin-right: 1rem !important; + } + + .ms-xxl-4 { + margin-right: 1.5rem !important; + } + + .ms-xxl-5 { + margin-right: 3rem !important; + } + + .ms-xxl-auto { + margin-right: auto !important; + } + + .p-xxl-0 { + padding: 0 !important; + } + + .p-xxl-1 { + padding: 0.25rem !important; + } + + .p-xxl-2 { + padding: 0.5rem !important; + } + + .p-xxl-3 { + padding: 1rem !important; + } + + .p-xxl-4 { + padding: 1.5rem !important; + } + + .p-xxl-5 { + padding: 3rem !important; + } + + .px-xxl-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-xxl-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-xxl-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-xxl-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-xxl-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-xxl-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-xxl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xxl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xxl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xxl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xxl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xxl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xxl-0 { + padding-top: 0 !important; + } + + .pt-xxl-1 { + padding-top: 0.25rem !important; + } + + .pt-xxl-2 { + padding-top: 0.5rem !important; + } + + .pt-xxl-3 { + padding-top: 1rem !important; + } + + .pt-xxl-4 { + padding-top: 1.5rem !important; + } + + .pt-xxl-5 { + padding-top: 3rem !important; + } + + .pe-xxl-0 { + padding-left: 0 !important; + } + + .pe-xxl-1 { + padding-left: 0.25rem !important; + } + + .pe-xxl-2 { + padding-left: 0.5rem !important; + } + + .pe-xxl-3 { + padding-left: 1rem !important; + } + + .pe-xxl-4 { + padding-left: 1.5rem !important; + } + + .pe-xxl-5 { + padding-left: 3rem !important; + } + + .pb-xxl-0 { + padding-bottom: 0 !important; + } + + .pb-xxl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xxl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xxl-3 { + padding-bottom: 1rem !important; + } + + .pb-xxl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xxl-5 { + padding-bottom: 3rem !important; + } + + .ps-xxl-0 { + padding-right: 0 !important; + } + + .ps-xxl-1 { + padding-right: 0.25rem !important; + } + + .ps-xxl-2 { + padding-right: 0.5rem !important; + } + + .ps-xxl-3 { + padding-right: 1rem !important; + } + + .ps-xxl-4 { + padding-right: 1.5rem !important; + } + + .ps-xxl-5 { + padding-right: 3rem !important; + } } + @media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-grid { + display: grid !important; + } + + .d-print-inline-grid { + display: inline-grid !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: flex !important; + } + + .d-print-inline-flex { + display: inline-flex !important; + } + + .d-print-none { + display: none !important; + } } -/*# sourceMappingURL=bootstrap-grid.rtl.css.map */ \ No newline at end of file + +/*# sourceMappingURL=bootstrap-grid.rtl.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css index 6305410923..ebe47f3b44 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css @@ -5,427 +5,435 @@ */ :root, [data-bs-theme=light] { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-black: #000; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #0d6efd; - --bs-secondary: #6c757d; - --bs-success: #198754; - --bs-info: #0dcaf0; - --bs-warning: #ffc107; - --bs-danger: #dc3545; - --bs-light: #f8f9fa; - --bs-dark: #212529; - --bs-primary-rgb: 13, 110, 253; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 25, 135, 84; - --bs-info-rgb: 13, 202, 240; - --bs-warning-rgb: 255, 193, 7; - --bs-danger-rgb: 220, 53, 69; - --bs-light-rgb: 248, 249, 250; - --bs-dark-rgb: 33, 37, 41; - --bs-primary-text-emphasis: #052c65; - --bs-secondary-text-emphasis: #2b2f32; - --bs-success-text-emphasis: #0a3622; - --bs-info-text-emphasis: #055160; - --bs-warning-text-emphasis: #664d03; - --bs-danger-text-emphasis: #58151c; - --bs-light-text-emphasis: #495057; - --bs-dark-text-emphasis: #495057; - --bs-primary-bg-subtle: #cfe2ff; - --bs-secondary-bg-subtle: #e2e3e5; - --bs-success-bg-subtle: #d1e7dd; - --bs-info-bg-subtle: #cff4fc; - --bs-warning-bg-subtle: #fff3cd; - --bs-danger-bg-subtle: #f8d7da; - --bs-light-bg-subtle: #fcfcfd; - --bs-dark-bg-subtle: #ced4da; - --bs-primary-border-subtle: #9ec5fe; - --bs-secondary-border-subtle: #c4c8cb; - --bs-success-border-subtle: #a3cfbb; - --bs-info-border-subtle: #9eeaf9; - --bs-warning-border-subtle: #ffe69c; - --bs-danger-border-subtle: #f1aeb5; - --bs-light-border-subtle: #e9ecef; - --bs-dark-border-subtle: #adb5bd; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-body-font-family: var(--bs-font-sans-serif); - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #212529; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg: #fff; - --bs-body-bg-rgb: 255, 255, 255; - --bs-emphasis-color: #000; - --bs-emphasis-color-rgb: 0, 0, 0; - --bs-secondary-color: rgba(33, 37, 41, 0.75); - --bs-secondary-color-rgb: 33, 37, 41; - --bs-secondary-bg: #e9ecef; - --bs-secondary-bg-rgb: 233, 236, 239; - --bs-tertiary-color: rgba(33, 37, 41, 0.5); - --bs-tertiary-color-rgb: 33, 37, 41; - --bs-tertiary-bg: #f8f9fa; - --bs-tertiary-bg-rgb: 248, 249, 250; - --bs-heading-color: inherit; - --bs-link-color: #0d6efd; - --bs-link-color-rgb: 13, 110, 253; - --bs-link-decoration: underline; - --bs-link-hover-color: #0a58ca; - --bs-link-hover-color-rgb: 10, 88, 202; - --bs-code-color: #d63384; - --bs-highlight-color: #212529; - --bs-highlight-bg: #fff3cd; - --bs-border-width: 1px; - --bs-border-style: solid; - --bs-border-color: #dee2e6; - --bs-border-color-translucent: rgba(0, 0, 0, 0.175); - --bs-border-radius: 0.375rem; - --bs-border-radius-sm: 0.25rem; - --bs-border-radius-lg: 0.5rem; - --bs-border-radius-xl: 1rem; - --bs-border-radius-xxl: 2rem; - --bs-border-radius-2xl: var(--bs-border-radius-xxl); - --bs-border-radius-pill: 50rem; - --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); - --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); - --bs-focus-ring-width: 0.25rem; - --bs-focus-ring-opacity: 0.25; - --bs-focus-ring-color: rgba(13, 110, 253, 0.25); - --bs-form-valid-color: #198754; - --bs-form-valid-border-color: #198754; - --bs-form-invalid-color: #dc3545; - --bs-form-invalid-border-color: #dc3545; + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-black: #000; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #0d6efd; + --bs-secondary: #6c757d; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-primary-rgb: 13, 110, 253; + --bs-secondary-rgb: 108, 117, 125; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-primary-text-emphasis: #052c65; + --bs-secondary-text-emphasis: #2b2f32; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #cfe2ff; + --bs-secondary-bg-subtle: #e2e3e5; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-primary-border-subtle: #9ec5fe; + --bs-secondary-border-subtle: #c4c8cb; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-body-font-family: var(--bs-font-sans-serif); + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #0d6efd; + --bs-link-color-rgb: 13, 110, 253; + --bs-link-decoration: underline; + --bs-link-hover-color: #0a58ca; + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-code-color: #d63384; + --bs-highlight-color: #212529; + --bs-highlight-bg: #fff3cd; + --bs-border-width: 1px; + --bs-border-style: solid; + --bs-border-color: #dee2e6; + --bs-border-color-translucent: rgba(0, 0, 0, 0.175); + --bs-border-radius: 0.375rem; + --bs-border-radius-sm: 0.25rem; + --bs-border-radius-lg: 0.5rem; + --bs-border-radius-xl: 1rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); + --bs-border-radius-pill: 50rem; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(13, 110, 253, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; } [data-bs-theme=dark] { - color-scheme: dark; - --bs-body-color: #dee2e6; - --bs-body-color-rgb: 222, 226, 230; - --bs-body-bg: #212529; - --bs-body-bg-rgb: 33, 37, 41; - --bs-emphasis-color: #fff; - --bs-emphasis-color-rgb: 255, 255, 255; - --bs-secondary-color: rgba(222, 226, 230, 0.75); - --bs-secondary-color-rgb: 222, 226, 230; - --bs-secondary-bg: #343a40; - --bs-secondary-bg-rgb: 52, 58, 64; - --bs-tertiary-color: rgba(222, 226, 230, 0.5); - --bs-tertiary-color-rgb: 222, 226, 230; - --bs-tertiary-bg: #2b3035; - --bs-tertiary-bg-rgb: 43, 48, 53; - --bs-primary-text-emphasis: #6ea8fe; - --bs-secondary-text-emphasis: #a7acb1; - --bs-success-text-emphasis: #75b798; - --bs-info-text-emphasis: #6edff6; - --bs-warning-text-emphasis: #ffda6a; - --bs-danger-text-emphasis: #ea868f; - --bs-light-text-emphasis: #f8f9fa; - --bs-dark-text-emphasis: #dee2e6; - --bs-primary-bg-subtle: #031633; - --bs-secondary-bg-subtle: #161719; - --bs-success-bg-subtle: #051b11; - --bs-info-bg-subtle: #032830; - --bs-warning-bg-subtle: #332701; - --bs-danger-bg-subtle: #2c0b0e; - --bs-light-bg-subtle: #343a40; - --bs-dark-bg-subtle: #1a1d20; - --bs-primary-border-subtle: #084298; - --bs-secondary-border-subtle: #41464b; - --bs-success-border-subtle: #0f5132; - --bs-info-border-subtle: #087990; - --bs-warning-border-subtle: #997404; - --bs-danger-border-subtle: #842029; - --bs-light-border-subtle: #495057; - --bs-dark-border-subtle: #343a40; - --bs-heading-color: inherit; - --bs-link-color: #6ea8fe; - --bs-link-hover-color: #8bb9fe; - --bs-link-color-rgb: 110, 168, 254; - --bs-link-hover-color-rgb: 139, 185, 254; - --bs-code-color: #e685b5; - --bs-highlight-color: #dee2e6; - --bs-highlight-bg: #664d03; - --bs-border-color: #495057; - --bs-border-color-translucent: rgba(255, 255, 255, 0.15); - --bs-form-valid-color: #75b798; - --bs-form-valid-border-color: #75b798; - --bs-form-invalid-color: #ea868f; - --bs-form-invalid-border-color: #ea868f; + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #6ea8fe; + --bs-secondary-text-emphasis: #a7acb1; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-primary-bg-subtle: #031633; + --bs-secondary-bg-subtle: #161719; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-primary-border-subtle: #084298; + --bs-secondary-border-subtle: #41464b; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-heading-color: inherit; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-code-color: #e685b5; + --bs-highlight-color: #dee2e6; + --bs-highlight-bg: #664d03; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; } *, *::before, *::after { - box-sizing: border-box; + box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { - :root { - scroll-behavior: smooth; - } + :root { + scroll-behavior: smooth; + } } body { - margin: 0; - font-family: var(--bs-body-font-family); - font-size: var(--bs-body-font-size); - font-weight: var(--bs-body-font-weight); - line-height: var(--bs-body-line-height); - color: var(--bs-body-color); - text-align: var(--bs-body-text-align); - background-color: var(--bs-body-bg); - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + margin: 0; + font-family: var(--bs-body-font-family); + font-size: var(--bs-body-font-size); + font-weight: var(--bs-body-font-weight); + line-height: var(--bs-body-line-height); + color: var(--bs-body-color); + text-align: var(--bs-body-text-align); + background-color: var(--bs-body-bg); + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { - margin: 1rem 0; - color: inherit; - border: 0; - border-top: var(--bs-border-width) solid; - opacity: 0.25; + margin: 1rem 0; + color: inherit; + border: 0; + border-top: var(--bs-border-width) solid; + opacity: 0.25; } h6, h5, h4, h3, h2, h1 { - margin-top: 0; - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; - color: var(--bs-heading-color); + margin-top: 0; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; + color: var(--bs-heading-color); } h1 { - font-size: calc(1.375rem + 1.5vw); + font-size: calc(1.375rem + 1.5vw); } + @media (min-width: 1200px) { - h1 { - font-size: 2.5rem; - } + h1 { + font-size: 2.5rem; + } } h2 { - font-size: calc(1.325rem + 0.9vw); + font-size: calc(1.325rem + 0.9vw); } + @media (min-width: 1200px) { - h2 { - font-size: 2rem; - } + h2 { + font-size: 2rem; + } } h3 { - font-size: calc(1.3rem + 0.6vw); + font-size: calc(1.3rem + 0.6vw); } + @media (min-width: 1200px) { - h3 { - font-size: 1.75rem; - } + h3 { + font-size: 1.75rem; + } } h4 { - font-size: calc(1.275rem + 0.3vw); + font-size: calc(1.275rem + 0.3vw); } + @media (min-width: 1200px) { - h4 { - font-size: 1.5rem; - } + h4 { + font-size: 1.5rem; + } } h5 { - font-size: 1.25rem; + font-size: 1.25rem; } h6 { - font-size: 1rem; + font-size: 1rem; } p { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } abbr[title] { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none; } address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; } ol, ul { - padding-left: 2rem; + padding-left: 2rem; } ol, ul, dl { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { - margin-bottom: 0; + margin-bottom: 0; } dt { - font-weight: 700; + font-weight: 700; } dd { - margin-bottom: 0.5rem; - margin-left: 0; + margin-bottom: 0.5rem; + margin-left: 0; } blockquote { - margin: 0 0 1rem; + margin: 0 0 1rem; } b, strong { - font-weight: bolder; + font-weight: bolder; } small { - font-size: 0.875em; + font-size: 0.875em; } mark { - padding: 0.1875em; - color: var(--bs-highlight-color); - background-color: var(--bs-highlight-bg); + padding: 0.1875em; + color: var(--bs-highlight-color); + background-color: var(--bs-highlight-bg); } sub, sup { - position: relative; - font-size: 0.75em; - line-height: 0; - vertical-align: baseline; + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; } sub { - bottom: -0.25em; + bottom: -0.25em; } sup { - top: -0.5em; + top: -0.5em; } a { - color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); - text-decoration: underline; + color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); + text-decoration: underline; } + a:hover { - --bs-link-color-rgb: var(--bs-link-hover-color-rgb); + --bs-link-color-rgb: var(--bs-link-hover-color-rgb); } a:not([href]):not([class]), a:not([href]):not([class]):hover { - color: inherit; - text-decoration: none; + color: inherit; + text-decoration: none; } pre, code, kbd, samp { - font-family: var(--bs-font-monospace); - font-size: 1em; + font-family: var(--bs-font-monospace); + font-size: 1em; } pre { - display: block; - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - font-size: 0.875em; + display: block; + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + font-size: 0.875em; } + pre code { - font-size: inherit; - color: inherit; - word-break: normal; + font-size: inherit; + color: inherit; + word-break: normal; } code { - font-size: 0.875em; - color: var(--bs-code-color); - word-wrap: break-word; + font-size: 0.875em; + color: var(--bs-code-color); + word-wrap: break-word; } + a > code { - color: inherit; + color: inherit; } kbd { - padding: 0.1875rem 0.375rem; - font-size: 0.875em; - color: var(--bs-body-bg); - background-color: var(--bs-body-color); - border-radius: 0.25rem; + padding: 0.1875rem 0.375rem; + font-size: 0.875em; + color: var(--bs-body-bg); + background-color: var(--bs-body-color); + border-radius: 0.25rem; } + kbd kbd { - padding: 0; - font-size: 1em; + padding: 0; + font-size: 1em; } figure { - margin: 0 0 1rem; + margin: 0 0 1rem; } img, svg { - vertical-align: middle; + vertical-align: middle; } table { - caption-side: bottom; - border-collapse: collapse; + caption-side: bottom; + border-collapse: collapse; } caption { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: var(--bs-secondary-color); - text-align: left; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-secondary-color); + text-align: left; } th { - text-align: inherit; - text-align: -webkit-match-parent; + text-align: inherit; + text-align: -webkit-match-parent; } thead, @@ -434,21 +442,21 @@ tfoot, tr, td, th { - border-color: inherit; - border-style: solid; - border-width: 0; + border-color: inherit; + border-style: solid; + border-width: 0; } label { - display: inline-block; + display: inline-block; } button { - border-radius: 0; + border-radius: 0; } button:focus:not(:focus-visible) { - outline: 0; + outline: 0; } input, @@ -456,76 +464,80 @@ button, select, optgroup, textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; } button, select { - text-transform: none; + text-transform: none; } [role=button] { - cursor: pointer; + cursor: pointer; } select { - word-wrap: normal; + word-wrap: normal; } + select:disabled { - opacity: 1; + opacity: 1; } [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { - display: none !important; + display: none !important; } button, [type=button], [type=reset], [type=submit] { - -webkit-appearance: button; + -webkit-appearance: button; } + button:not(:disabled), [type=button]:not(:disabled), [type=reset]:not(:disabled), [type=submit]:not(:disabled) { - cursor: pointer; + cursor: pointer; } ::-moz-focus-inner { - padding: 0; - border-style: none; + padding: 0; + border-style: none; } textarea { - resize: vertical; + resize: vertical; } fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; + min-width: 0; + padding: 0; + margin: 0; + border: 0; } legend { - float: left; - width: 100%; - padding: 0; - margin-bottom: 0.5rem; - font-size: calc(1.275rem + 0.3vw); - line-height: inherit; + float: left; + width: 100%; + padding: 0; + margin-bottom: 0.5rem; + font-size: calc(1.275rem + 0.3vw); + line-height: inherit; } + @media (min-width: 1200px) { - legend { - font-size: 1.5rem; - } + legend { + font-size: 1.5rem; + } } + legend + * { - clear: left; + clear: left; } ::-webkit-datetime-edit-fields-wrapper, @@ -535,16 +547,16 @@ legend + * { ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { - padding: 0; + padding: 0; } ::-webkit-inner-spin-button { - height: auto; + height: auto; } [type=search] { - -webkit-appearance: textfield; - outline-offset: -2px; + -webkit-appearance: textfield; + outline-offset: -2px; } /* rtl:raw: @@ -556,42 +568,42 @@ legend + * { } */ ::-webkit-search-decoration { - -webkit-appearance: none; + -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { - padding: 0; + padding: 0; } ::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } ::file-selector-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } output { - display: inline-block; + display: inline-block; } iframe { - border: 0; + border: 0; } summary { - display: list-item; - cursor: pointer; + display: list-item; + cursor: pointer; } progress { - vertical-align: baseline; + vertical-align: baseline; } [hidden] { - display: none !important; + display: none !important; } -/*# sourceMappingURL=bootstrap-reboot.css.map */ \ No newline at end of file +/*# sourceMappingURL=bootstrap-reboot.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css index 2103919f4c..1457b46e22 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css @@ -5,427 +5,435 @@ */ :root, [data-bs-theme=light] { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-black: #000; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #0d6efd; - --bs-secondary: #6c757d; - --bs-success: #198754; - --bs-info: #0dcaf0; - --bs-warning: #ffc107; - --bs-danger: #dc3545; - --bs-light: #f8f9fa; - --bs-dark: #212529; - --bs-primary-rgb: 13, 110, 253; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 25, 135, 84; - --bs-info-rgb: 13, 202, 240; - --bs-warning-rgb: 255, 193, 7; - --bs-danger-rgb: 220, 53, 69; - --bs-light-rgb: 248, 249, 250; - --bs-dark-rgb: 33, 37, 41; - --bs-primary-text-emphasis: #052c65; - --bs-secondary-text-emphasis: #2b2f32; - --bs-success-text-emphasis: #0a3622; - --bs-info-text-emphasis: #055160; - --bs-warning-text-emphasis: #664d03; - --bs-danger-text-emphasis: #58151c; - --bs-light-text-emphasis: #495057; - --bs-dark-text-emphasis: #495057; - --bs-primary-bg-subtle: #cfe2ff; - --bs-secondary-bg-subtle: #e2e3e5; - --bs-success-bg-subtle: #d1e7dd; - --bs-info-bg-subtle: #cff4fc; - --bs-warning-bg-subtle: #fff3cd; - --bs-danger-bg-subtle: #f8d7da; - --bs-light-bg-subtle: #fcfcfd; - --bs-dark-bg-subtle: #ced4da; - --bs-primary-border-subtle: #9ec5fe; - --bs-secondary-border-subtle: #c4c8cb; - --bs-success-border-subtle: #a3cfbb; - --bs-info-border-subtle: #9eeaf9; - --bs-warning-border-subtle: #ffe69c; - --bs-danger-border-subtle: #f1aeb5; - --bs-light-border-subtle: #e9ecef; - --bs-dark-border-subtle: #adb5bd; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-body-font-family: var(--bs-font-sans-serif); - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #212529; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg: #fff; - --bs-body-bg-rgb: 255, 255, 255; - --bs-emphasis-color: #000; - --bs-emphasis-color-rgb: 0, 0, 0; - --bs-secondary-color: rgba(33, 37, 41, 0.75); - --bs-secondary-color-rgb: 33, 37, 41; - --bs-secondary-bg: #e9ecef; - --bs-secondary-bg-rgb: 233, 236, 239; - --bs-tertiary-color: rgba(33, 37, 41, 0.5); - --bs-tertiary-color-rgb: 33, 37, 41; - --bs-tertiary-bg: #f8f9fa; - --bs-tertiary-bg-rgb: 248, 249, 250; - --bs-heading-color: inherit; - --bs-link-color: #0d6efd; - --bs-link-color-rgb: 13, 110, 253; - --bs-link-decoration: underline; - --bs-link-hover-color: #0a58ca; - --bs-link-hover-color-rgb: 10, 88, 202; - --bs-code-color: #d63384; - --bs-highlight-color: #212529; - --bs-highlight-bg: #fff3cd; - --bs-border-width: 1px; - --bs-border-style: solid; - --bs-border-color: #dee2e6; - --bs-border-color-translucent: rgba(0, 0, 0, 0.175); - --bs-border-radius: 0.375rem; - --bs-border-radius-sm: 0.25rem; - --bs-border-radius-lg: 0.5rem; - --bs-border-radius-xl: 1rem; - --bs-border-radius-xxl: 2rem; - --bs-border-radius-2xl: var(--bs-border-radius-xxl); - --bs-border-radius-pill: 50rem; - --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); - --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); - --bs-focus-ring-width: 0.25rem; - --bs-focus-ring-opacity: 0.25; - --bs-focus-ring-color: rgba(13, 110, 253, 0.25); - --bs-form-valid-color: #198754; - --bs-form-valid-border-color: #198754; - --bs-form-invalid-color: #dc3545; - --bs-form-invalid-border-color: #dc3545; + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-black: #000; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #0d6efd; + --bs-secondary: #6c757d; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-primary-rgb: 13, 110, 253; + --bs-secondary-rgb: 108, 117, 125; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-primary-text-emphasis: #052c65; + --bs-secondary-text-emphasis: #2b2f32; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #cfe2ff; + --bs-secondary-bg-subtle: #e2e3e5; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-primary-border-subtle: #9ec5fe; + --bs-secondary-border-subtle: #c4c8cb; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-body-font-family: var(--bs-font-sans-serif); + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #0d6efd; + --bs-link-color-rgb: 13, 110, 253; + --bs-link-decoration: underline; + --bs-link-hover-color: #0a58ca; + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-code-color: #d63384; + --bs-highlight-color: #212529; + --bs-highlight-bg: #fff3cd; + --bs-border-width: 1px; + --bs-border-style: solid; + --bs-border-color: #dee2e6; + --bs-border-color-translucent: rgba(0, 0, 0, 0.175); + --bs-border-radius: 0.375rem; + --bs-border-radius-sm: 0.25rem; + --bs-border-radius-lg: 0.5rem; + --bs-border-radius-xl: 1rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); + --bs-border-radius-pill: 50rem; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(13, 110, 253, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; } [data-bs-theme=dark] { - color-scheme: dark; - --bs-body-color: #dee2e6; - --bs-body-color-rgb: 222, 226, 230; - --bs-body-bg: #212529; - --bs-body-bg-rgb: 33, 37, 41; - --bs-emphasis-color: #fff; - --bs-emphasis-color-rgb: 255, 255, 255; - --bs-secondary-color: rgba(222, 226, 230, 0.75); - --bs-secondary-color-rgb: 222, 226, 230; - --bs-secondary-bg: #343a40; - --bs-secondary-bg-rgb: 52, 58, 64; - --bs-tertiary-color: rgba(222, 226, 230, 0.5); - --bs-tertiary-color-rgb: 222, 226, 230; - --bs-tertiary-bg: #2b3035; - --bs-tertiary-bg-rgb: 43, 48, 53; - --bs-primary-text-emphasis: #6ea8fe; - --bs-secondary-text-emphasis: #a7acb1; - --bs-success-text-emphasis: #75b798; - --bs-info-text-emphasis: #6edff6; - --bs-warning-text-emphasis: #ffda6a; - --bs-danger-text-emphasis: #ea868f; - --bs-light-text-emphasis: #f8f9fa; - --bs-dark-text-emphasis: #dee2e6; - --bs-primary-bg-subtle: #031633; - --bs-secondary-bg-subtle: #161719; - --bs-success-bg-subtle: #051b11; - --bs-info-bg-subtle: #032830; - --bs-warning-bg-subtle: #332701; - --bs-danger-bg-subtle: #2c0b0e; - --bs-light-bg-subtle: #343a40; - --bs-dark-bg-subtle: #1a1d20; - --bs-primary-border-subtle: #084298; - --bs-secondary-border-subtle: #41464b; - --bs-success-border-subtle: #0f5132; - --bs-info-border-subtle: #087990; - --bs-warning-border-subtle: #997404; - --bs-danger-border-subtle: #842029; - --bs-light-border-subtle: #495057; - --bs-dark-border-subtle: #343a40; - --bs-heading-color: inherit; - --bs-link-color: #6ea8fe; - --bs-link-hover-color: #8bb9fe; - --bs-link-color-rgb: 110, 168, 254; - --bs-link-hover-color-rgb: 139, 185, 254; - --bs-code-color: #e685b5; - --bs-highlight-color: #dee2e6; - --bs-highlight-bg: #664d03; - --bs-border-color: #495057; - --bs-border-color-translucent: rgba(255, 255, 255, 0.15); - --bs-form-valid-color: #75b798; - --bs-form-valid-border-color: #75b798; - --bs-form-invalid-color: #ea868f; - --bs-form-invalid-border-color: #ea868f; + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #6ea8fe; + --bs-secondary-text-emphasis: #a7acb1; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-primary-bg-subtle: #031633; + --bs-secondary-bg-subtle: #161719; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-primary-border-subtle: #084298; + --bs-secondary-border-subtle: #41464b; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-heading-color: inherit; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-code-color: #e685b5; + --bs-highlight-color: #dee2e6; + --bs-highlight-bg: #664d03; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; } *, *::before, *::after { - box-sizing: border-box; + box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { - :root { - scroll-behavior: smooth; - } + :root { + scroll-behavior: smooth; + } } body { - margin: 0; - font-family: var(--bs-body-font-family); - font-size: var(--bs-body-font-size); - font-weight: var(--bs-body-font-weight); - line-height: var(--bs-body-line-height); - color: var(--bs-body-color); - text-align: var(--bs-body-text-align); - background-color: var(--bs-body-bg); - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + margin: 0; + font-family: var(--bs-body-font-family); + font-size: var(--bs-body-font-size); + font-weight: var(--bs-body-font-weight); + line-height: var(--bs-body-line-height); + color: var(--bs-body-color); + text-align: var(--bs-body-text-align); + background-color: var(--bs-body-bg); + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { - margin: 1rem 0; - color: inherit; - border: 0; - border-top: var(--bs-border-width) solid; - opacity: 0.25; + margin: 1rem 0; + color: inherit; + border: 0; + border-top: var(--bs-border-width) solid; + opacity: 0.25; } h6, h5, h4, h3, h2, h1 { - margin-top: 0; - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; - color: var(--bs-heading-color); + margin-top: 0; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; + color: var(--bs-heading-color); } h1 { - font-size: calc(1.375rem + 1.5vw); + font-size: calc(1.375rem + 1.5vw); } + @media (min-width: 1200px) { - h1 { - font-size: 2.5rem; - } + h1 { + font-size: 2.5rem; + } } h2 { - font-size: calc(1.325rem + 0.9vw); + font-size: calc(1.325rem + 0.9vw); } + @media (min-width: 1200px) { - h2 { - font-size: 2rem; - } + h2 { + font-size: 2rem; + } } h3 { - font-size: calc(1.3rem + 0.6vw); + font-size: calc(1.3rem + 0.6vw); } + @media (min-width: 1200px) { - h3 { - font-size: 1.75rem; - } + h3 { + font-size: 1.75rem; + } } h4 { - font-size: calc(1.275rem + 0.3vw); + font-size: calc(1.275rem + 0.3vw); } + @media (min-width: 1200px) { - h4 { - font-size: 1.5rem; - } + h4 { + font-size: 1.5rem; + } } h5 { - font-size: 1.25rem; + font-size: 1.25rem; } h6 { - font-size: 1rem; + font-size: 1rem; } p { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } abbr[title] { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none; } address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; } ol, ul { - padding-right: 2rem; + padding-right: 2rem; } ol, ul, dl { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { - margin-bottom: 0; + margin-bottom: 0; } dt { - font-weight: 700; + font-weight: 700; } dd { - margin-bottom: 0.5rem; - margin-right: 0; + margin-bottom: 0.5rem; + margin-right: 0; } blockquote { - margin: 0 0 1rem; + margin: 0 0 1rem; } b, strong { - font-weight: bolder; + font-weight: bolder; } small { - font-size: 0.875em; + font-size: 0.875em; } mark { - padding: 0.1875em; - color: var(--bs-highlight-color); - background-color: var(--bs-highlight-bg); + padding: 0.1875em; + color: var(--bs-highlight-color); + background-color: var(--bs-highlight-bg); } sub, sup { - position: relative; - font-size: 0.75em; - line-height: 0; - vertical-align: baseline; + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; } sub { - bottom: -0.25em; + bottom: -0.25em; } sup { - top: -0.5em; + top: -0.5em; } a { - color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); - text-decoration: underline; + color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); + text-decoration: underline; } + a:hover { - --bs-link-color-rgb: var(--bs-link-hover-color-rgb); + --bs-link-color-rgb: var(--bs-link-hover-color-rgb); } a:not([href]):not([class]), a:not([href]):not([class]):hover { - color: inherit; - text-decoration: none; + color: inherit; + text-decoration: none; } pre, code, kbd, samp { - font-family: var(--bs-font-monospace); - font-size: 1em; + font-family: var(--bs-font-monospace); + font-size: 1em; } pre { - display: block; - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - font-size: 0.875em; + display: block; + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + font-size: 0.875em; } + pre code { - font-size: inherit; - color: inherit; - word-break: normal; + font-size: inherit; + color: inherit; + word-break: normal; } code { - font-size: 0.875em; - color: var(--bs-code-color); - word-wrap: break-word; + font-size: 0.875em; + color: var(--bs-code-color); + word-wrap: break-word; } + a > code { - color: inherit; + color: inherit; } kbd { - padding: 0.1875rem 0.375rem; - font-size: 0.875em; - color: var(--bs-body-bg); - background-color: var(--bs-body-color); - border-radius: 0.25rem; + padding: 0.1875rem 0.375rem; + font-size: 0.875em; + color: var(--bs-body-bg); + background-color: var(--bs-body-color); + border-radius: 0.25rem; } + kbd kbd { - padding: 0; - font-size: 1em; + padding: 0; + font-size: 1em; } figure { - margin: 0 0 1rem; + margin: 0 0 1rem; } img, svg { - vertical-align: middle; + vertical-align: middle; } table { - caption-side: bottom; - border-collapse: collapse; + caption-side: bottom; + border-collapse: collapse; } caption { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: var(--bs-secondary-color); - text-align: right; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-secondary-color); + text-align: right; } th { - text-align: inherit; - text-align: -webkit-match-parent; + text-align: inherit; + text-align: -webkit-match-parent; } thead, @@ -434,21 +442,21 @@ tfoot, tr, td, th { - border-color: inherit; - border-style: solid; - border-width: 0; + border-color: inherit; + border-style: solid; + border-width: 0; } label { - display: inline-block; + display: inline-block; } button { - border-radius: 0; + border-radius: 0; } button:focus:not(:focus-visible) { - outline: 0; + outline: 0; } input, @@ -456,76 +464,80 @@ button, select, optgroup, textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; } button, select { - text-transform: none; + text-transform: none; } [role=button] { - cursor: pointer; + cursor: pointer; } select { - word-wrap: normal; + word-wrap: normal; } + select:disabled { - opacity: 1; + opacity: 1; } [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { - display: none !important; + display: none !important; } button, [type=button], [type=reset], [type=submit] { - -webkit-appearance: button; + -webkit-appearance: button; } + button:not(:disabled), [type=button]:not(:disabled), [type=reset]:not(:disabled), [type=submit]:not(:disabled) { - cursor: pointer; + cursor: pointer; } ::-moz-focus-inner { - padding: 0; - border-style: none; + padding: 0; + border-style: none; } textarea { - resize: vertical; + resize: vertical; } fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; + min-width: 0; + padding: 0; + margin: 0; + border: 0; } legend { - float: right; - width: 100%; - padding: 0; - margin-bottom: 0.5rem; - font-size: calc(1.275rem + 0.3vw); - line-height: inherit; + float: right; + width: 100%; + padding: 0; + margin-bottom: 0.5rem; + font-size: calc(1.275rem + 0.3vw); + line-height: inherit; } + @media (min-width: 1200px) { - legend { - font-size: 1.5rem; - } + legend { + font-size: 1.5rem; + } } + legend + * { - clear: right; + clear: right; } ::-webkit-datetime-edit-fields-wrapper, @@ -535,60 +547,62 @@ legend + * { ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { - padding: 0; + padding: 0; } ::-webkit-inner-spin-button { - height: auto; + height: auto; } [type=search] { - -webkit-appearance: textfield; - outline-offset: -2px; + -webkit-appearance: textfield; + outline-offset: -2px; } [type="tel"], [type="url"], [type="email"], [type="number"] { - direction: ltr; + direction: ltr; } + ::-webkit-search-decoration { - -webkit-appearance: none; + -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { - padding: 0; + padding: 0; } ::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } ::file-selector-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } output { - display: inline-block; + display: inline-block; } iframe { - border: 0; + border: 0; } summary { - display: list-item; - cursor: pointer; + display: list-item; + cursor: pointer; } progress { - vertical-align: baseline; + vertical-align: baseline; } [hidden] { - display: none !important; + display: none !important; } -/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ \ No newline at end of file + +/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css index aaa6379278..5f79f29856 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css @@ -5,5398 +5,6306 @@ */ :root, [data-bs-theme=light] { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-black: #000; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #0d6efd; - --bs-secondary: #6c757d; - --bs-success: #198754; - --bs-info: #0dcaf0; - --bs-warning: #ffc107; - --bs-danger: #dc3545; - --bs-light: #f8f9fa; - --bs-dark: #212529; - --bs-primary-rgb: 13, 110, 253; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 25, 135, 84; - --bs-info-rgb: 13, 202, 240; - --bs-warning-rgb: 255, 193, 7; - --bs-danger-rgb: 220, 53, 69; - --bs-light-rgb: 248, 249, 250; - --bs-dark-rgb: 33, 37, 41; - --bs-primary-text-emphasis: #052c65; - --bs-secondary-text-emphasis: #2b2f32; - --bs-success-text-emphasis: #0a3622; - --bs-info-text-emphasis: #055160; - --bs-warning-text-emphasis: #664d03; - --bs-danger-text-emphasis: #58151c; - --bs-light-text-emphasis: #495057; - --bs-dark-text-emphasis: #495057; - --bs-primary-bg-subtle: #cfe2ff; - --bs-secondary-bg-subtle: #e2e3e5; - --bs-success-bg-subtle: #d1e7dd; - --bs-info-bg-subtle: #cff4fc; - --bs-warning-bg-subtle: #fff3cd; - --bs-danger-bg-subtle: #f8d7da; - --bs-light-bg-subtle: #fcfcfd; - --bs-dark-bg-subtle: #ced4da; - --bs-primary-border-subtle: #9ec5fe; - --bs-secondary-border-subtle: #c4c8cb; - --bs-success-border-subtle: #a3cfbb; - --bs-info-border-subtle: #9eeaf9; - --bs-warning-border-subtle: #ffe69c; - --bs-danger-border-subtle: #f1aeb5; - --bs-light-border-subtle: #e9ecef; - --bs-dark-border-subtle: #adb5bd; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-body-font-family: var(--bs-font-sans-serif); - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #212529; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg: #fff; - --bs-body-bg-rgb: 255, 255, 255; - --bs-emphasis-color: #000; - --bs-emphasis-color-rgb: 0, 0, 0; - --bs-secondary-color: rgba(33, 37, 41, 0.75); - --bs-secondary-color-rgb: 33, 37, 41; - --bs-secondary-bg: #e9ecef; - --bs-secondary-bg-rgb: 233, 236, 239; - --bs-tertiary-color: rgba(33, 37, 41, 0.5); - --bs-tertiary-color-rgb: 33, 37, 41; - --bs-tertiary-bg: #f8f9fa; - --bs-tertiary-bg-rgb: 248, 249, 250; - --bs-heading-color: inherit; - --bs-link-color: #0d6efd; - --bs-link-color-rgb: 13, 110, 253; - --bs-link-decoration: underline; - --bs-link-hover-color: #0a58ca; - --bs-link-hover-color-rgb: 10, 88, 202; - --bs-code-color: #d63384; - --bs-highlight-color: #212529; - --bs-highlight-bg: #fff3cd; - --bs-border-width: 1px; - --bs-border-style: solid; - --bs-border-color: #dee2e6; - --bs-border-color-translucent: rgba(0, 0, 0, 0.175); - --bs-border-radius: 0.375rem; - --bs-border-radius-sm: 0.25rem; - --bs-border-radius-lg: 0.5rem; - --bs-border-radius-xl: 1rem; - --bs-border-radius-xxl: 2rem; - --bs-border-radius-2xl: var(--bs-border-radius-xxl); - --bs-border-radius-pill: 50rem; - --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); - --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); - --bs-focus-ring-width: 0.25rem; - --bs-focus-ring-opacity: 0.25; - --bs-focus-ring-color: rgba(13, 110, 253, 0.25); - --bs-form-valid-color: #198754; - --bs-form-valid-border-color: #198754; - --bs-form-invalid-color: #dc3545; - --bs-form-invalid-border-color: #dc3545; + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-black: #000; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #0d6efd; + --bs-secondary: #6c757d; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-primary-rgb: 13, 110, 253; + --bs-secondary-rgb: 108, 117, 125; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-primary-text-emphasis: #052c65; + --bs-secondary-text-emphasis: #2b2f32; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #cfe2ff; + --bs-secondary-bg-subtle: #e2e3e5; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-primary-border-subtle: #9ec5fe; + --bs-secondary-border-subtle: #c4c8cb; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-body-font-family: var(--bs-font-sans-serif); + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #0d6efd; + --bs-link-color-rgb: 13, 110, 253; + --bs-link-decoration: underline; + --bs-link-hover-color: #0a58ca; + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-code-color: #d63384; + --bs-highlight-color: #212529; + --bs-highlight-bg: #fff3cd; + --bs-border-width: 1px; + --bs-border-style: solid; + --bs-border-color: #dee2e6; + --bs-border-color-translucent: rgba(0, 0, 0, 0.175); + --bs-border-radius: 0.375rem; + --bs-border-radius-sm: 0.25rem; + --bs-border-radius-lg: 0.5rem; + --bs-border-radius-xl: 1rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); + --bs-border-radius-pill: 50rem; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(13, 110, 253, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; } [data-bs-theme=dark] { - color-scheme: dark; - --bs-body-color: #dee2e6; - --bs-body-color-rgb: 222, 226, 230; - --bs-body-bg: #212529; - --bs-body-bg-rgb: 33, 37, 41; - --bs-emphasis-color: #fff; - --bs-emphasis-color-rgb: 255, 255, 255; - --bs-secondary-color: rgba(222, 226, 230, 0.75); - --bs-secondary-color-rgb: 222, 226, 230; - --bs-secondary-bg: #343a40; - --bs-secondary-bg-rgb: 52, 58, 64; - --bs-tertiary-color: rgba(222, 226, 230, 0.5); - --bs-tertiary-color-rgb: 222, 226, 230; - --bs-tertiary-bg: #2b3035; - --bs-tertiary-bg-rgb: 43, 48, 53; - --bs-primary-text-emphasis: #6ea8fe; - --bs-secondary-text-emphasis: #a7acb1; - --bs-success-text-emphasis: #75b798; - --bs-info-text-emphasis: #6edff6; - --bs-warning-text-emphasis: #ffda6a; - --bs-danger-text-emphasis: #ea868f; - --bs-light-text-emphasis: #f8f9fa; - --bs-dark-text-emphasis: #dee2e6; - --bs-primary-bg-subtle: #031633; - --bs-secondary-bg-subtle: #161719; - --bs-success-bg-subtle: #051b11; - --bs-info-bg-subtle: #032830; - --bs-warning-bg-subtle: #332701; - --bs-danger-bg-subtle: #2c0b0e; - --bs-light-bg-subtle: #343a40; - --bs-dark-bg-subtle: #1a1d20; - --bs-primary-border-subtle: #084298; - --bs-secondary-border-subtle: #41464b; - --bs-success-border-subtle: #0f5132; - --bs-info-border-subtle: #087990; - --bs-warning-border-subtle: #997404; - --bs-danger-border-subtle: #842029; - --bs-light-border-subtle: #495057; - --bs-dark-border-subtle: #343a40; - --bs-heading-color: inherit; - --bs-link-color: #6ea8fe; - --bs-link-hover-color: #8bb9fe; - --bs-link-color-rgb: 110, 168, 254; - --bs-link-hover-color-rgb: 139, 185, 254; - --bs-code-color: #e685b5; - --bs-highlight-color: #dee2e6; - --bs-highlight-bg: #664d03; - --bs-border-color: #495057; - --bs-border-color-translucent: rgba(255, 255, 255, 0.15); - --bs-form-valid-color: #75b798; - --bs-form-valid-border-color: #75b798; - --bs-form-invalid-color: #ea868f; - --bs-form-invalid-border-color: #ea868f; + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #6ea8fe; + --bs-secondary-text-emphasis: #a7acb1; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-primary-bg-subtle: #031633; + --bs-secondary-bg-subtle: #161719; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-primary-border-subtle: #084298; + --bs-secondary-border-subtle: #41464b; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-heading-color: inherit; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-code-color: #e685b5; + --bs-highlight-color: #dee2e6; + --bs-highlight-bg: #664d03; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; } .clearfix::after { - display: block; - clear: both; - content: ""; + display: block; + clear: both; + content: ""; } .text-bg-primary { - color: #fff !important; - background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-secondary { - color: #fff !important; - background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-success { - color: #fff !important; - background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-info { - color: #000 !important; - background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-warning { - color: #000 !important; - background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-danger { - color: #fff !important; - background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-light { - color: #000 !important; - background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-dark { - color: #fff !important; - background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; } .link-primary { - color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-primary:hover, .link-primary:focus { - color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; } .link-secondary { - color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-secondary:hover, .link-secondary:focus { - color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; } .link-success { - color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-success:hover, .link-success:focus { - color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; } .link-info { - color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-info:hover, .link-info:focus { - color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; } .link-warning { - color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-warning:hover, .link-warning:focus { - color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; } .link-danger { - color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-danger:hover, .link-danger:focus { - color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; } .link-light { - color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-light:hover, .link-light:focus { - color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; } .link-dark { - color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-dark:hover, .link-dark:focus { - color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; } .link-body-emphasis { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-body-emphasis:hover, .link-body-emphasis:focus { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; } .focus-ring:focus { - outline: 0; - box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); + outline: 0; + box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); } .icon-link { - display: inline-flex; - gap: 0.375rem; - align-items: center; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-underline-offset: 0.25em; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; + display: inline-flex; + gap: 0.375rem; + align-items: center; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-underline-offset: 0.25em; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + .icon-link > .bi { - flex-shrink: 0; - width: 1em; - height: 1em; - fill: currentcolor; - transition: 0.2s ease-in-out transform; + flex-shrink: 0; + width: 1em; + height: 1em; + fill: currentcolor; + transition: 0.2s ease-in-out transform; } + @media (prefers-reduced-motion: reduce) { - .icon-link > .bi { - transition: none; - } + .icon-link > .bi { + transition: none; + } } .icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi { - transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); + transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); } .ratio { - position: relative; - width: 100%; + position: relative; + width: 100%; } + .ratio::before { - display: block; - padding-top: var(--bs-aspect-ratio); - content: ""; + display: block; + padding-top: var(--bs-aspect-ratio); + content: ""; } + .ratio > * { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; } .ratio-1x1 { - --bs-aspect-ratio: 100%; + --bs-aspect-ratio: 100%; } .ratio-4x3 { - --bs-aspect-ratio: 75%; + --bs-aspect-ratio: 75%; } .ratio-16x9 { - --bs-aspect-ratio: 56.25%; + --bs-aspect-ratio: 56.25%; } .ratio-21x9 { - --bs-aspect-ratio: 42.8571428571%; + --bs-aspect-ratio: 42.8571428571%; } .fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1030; + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; } .fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 1030; + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; } .sticky-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; -} - -.sticky-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; -} - -@media (min-width: 576px) { - .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; - } - .sticky-sm-bottom { +} + +.sticky-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; - } } + +@media (min-width: 576px) { + .sticky-sm-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-sm-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } +} + @media (min-width: 768px) { - .sticky-md-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-md-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-md-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-md-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 992px) { - .sticky-lg-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-lg-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-lg-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-lg-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1200px) { - .sticky-xl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1400px) { - .sticky-xxl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xxl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xxl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xxl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + .hstack { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; + display: flex; + flex-direction: row; + align-items: center; + align-self: stretch; } .vstack { - display: flex; - flex: 1 1 auto; - flex-direction: column; - align-self: stretch; + display: flex; + flex: 1 1 auto; + flex-direction: column; + align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; } + .visually-hidden:not(caption), .visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) { - position: absolute !important; + position: absolute !important; } .stretched-link::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + content: ""; } .text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .vr { - display: inline-block; - align-self: stretch; - width: var(--bs-border-width); - min-height: 1em; - background-color: currentcolor; - opacity: 0.25; + display: inline-block; + align-self: stretch; + width: var(--bs-border-width); + min-height: 1em; + background-color: currentcolor; + opacity: 0.25; } .align-baseline { - vertical-align: baseline !important; + vertical-align: baseline !important; } .align-top { - vertical-align: top !important; + vertical-align: top !important; } .align-middle { - vertical-align: middle !important; + vertical-align: middle !important; } .align-bottom { - vertical-align: bottom !important; + vertical-align: bottom !important; } .align-text-bottom { - vertical-align: text-bottom !important; + vertical-align: text-bottom !important; } .align-text-top { - vertical-align: text-top !important; + vertical-align: text-top !important; } .float-start { - float: left !important; + float: left !important; } .float-end { - float: right !important; + float: right !important; } .float-none { - float: none !important; + float: none !important; } .object-fit-contain { - -o-object-fit: contain !important; - object-fit: contain !important; + -o-object-fit: contain !important; + object-fit: contain !important; } .object-fit-cover { - -o-object-fit: cover !important; - object-fit: cover !important; + -o-object-fit: cover !important; + object-fit: cover !important; } .object-fit-fill { - -o-object-fit: fill !important; - object-fit: fill !important; + -o-object-fit: fill !important; + object-fit: fill !important; } .object-fit-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; + -o-object-fit: scale-down !important; + object-fit: scale-down !important; } .object-fit-none { - -o-object-fit: none !important; - object-fit: none !important; + -o-object-fit: none !important; + object-fit: none !important; } .opacity-0 { - opacity: 0 !important; + opacity: 0 !important; } .opacity-25 { - opacity: 0.25 !important; + opacity: 0.25 !important; } .opacity-50 { - opacity: 0.5 !important; + opacity: 0.5 !important; } .opacity-75 { - opacity: 0.75 !important; + opacity: 0.75 !important; } .opacity-100 { - opacity: 1 !important; + opacity: 1 !important; } .overflow-auto { - overflow: auto !important; + overflow: auto !important; } .overflow-hidden { - overflow: hidden !important; + overflow: hidden !important; } .overflow-visible { - overflow: visible !important; + overflow: visible !important; } .overflow-scroll { - overflow: scroll !important; + overflow: scroll !important; } .overflow-x-auto { - overflow-x: auto !important; + overflow-x: auto !important; } .overflow-x-hidden { - overflow-x: hidden !important; + overflow-x: hidden !important; } .overflow-x-visible { - overflow-x: visible !important; + overflow-x: visible !important; } .overflow-x-scroll { - overflow-x: scroll !important; + overflow-x: scroll !important; } .overflow-y-auto { - overflow-y: auto !important; + overflow-y: auto !important; } .overflow-y-hidden { - overflow-y: hidden !important; + overflow-y: hidden !important; } .overflow-y-visible { - overflow-y: visible !important; + overflow-y: visible !important; } .overflow-y-scroll { - overflow-y: scroll !important; + overflow-y: scroll !important; } .d-inline { - display: inline !important; + display: inline !important; } .d-inline-block { - display: inline-block !important; + display: inline-block !important; } .d-block { - display: block !important; + display: block !important; } .d-grid { - display: grid !important; + display: grid !important; } .d-inline-grid { - display: inline-grid !important; + display: inline-grid !important; } .d-table { - display: table !important; + display: table !important; } .d-table-row { - display: table-row !important; + display: table-row !important; } .d-table-cell { - display: table-cell !important; + display: table-cell !important; } .d-flex { - display: flex !important; + display: flex !important; } .d-inline-flex { - display: inline-flex !important; + display: inline-flex !important; } .d-none { - display: none !important; + display: none !important; } .shadow { - box-shadow: var(--bs-box-shadow) !important; + box-shadow: var(--bs-box-shadow) !important; } .shadow-sm { - box-shadow: var(--bs-box-shadow-sm) !important; + box-shadow: var(--bs-box-shadow-sm) !important; } .shadow-lg { - box-shadow: var(--bs-box-shadow-lg) !important; + box-shadow: var(--bs-box-shadow-lg) !important; } .shadow-none { - box-shadow: none !important; + box-shadow: none !important; } .focus-ring-primary { - --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-secondary { - --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-success { - --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-info { - --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-warning { - --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-danger { - --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-light { - --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-dark { - --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); } .position-static { - position: static !important; + position: static !important; } .position-relative { - position: relative !important; + position: relative !important; } .position-absolute { - position: absolute !important; + position: absolute !important; } .position-fixed { - position: fixed !important; + position: fixed !important; } .position-sticky { - position: -webkit-sticky !important; - position: sticky !important; + position: -webkit-sticky !important; + position: sticky !important; } .top-0 { - top: 0 !important; + top: 0 !important; } .top-50 { - top: 50% !important; + top: 50% !important; } .top-100 { - top: 100% !important; + top: 100% !important; } .bottom-0 { - bottom: 0 !important; + bottom: 0 !important; } .bottom-50 { - bottom: 50% !important; + bottom: 50% !important; } .bottom-100 { - bottom: 100% !important; + bottom: 100% !important; } .start-0 { - left: 0 !important; + left: 0 !important; } .start-50 { - left: 50% !important; + left: 50% !important; } .start-100 { - left: 100% !important; + left: 100% !important; } .end-0 { - right: 0 !important; + right: 0 !important; } .end-50 { - right: 50% !important; + right: 50% !important; } .end-100 { - right: 100% !important; + right: 100% !important; } .translate-middle { - transform: translate(-50%, -50%) !important; + transform: translate(-50%, -50%) !important; } .translate-middle-x { - transform: translateX(-50%) !important; + transform: translateX(-50%) !important; } .translate-middle-y { - transform: translateY(-50%) !important; + transform: translateY(-50%) !important; } .border { - border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-0 { - border: 0 !important; + border: 0 !important; } .border-top { - border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-top-0 { - border-top: 0 !important; + border-top: 0 !important; } .border-end { - border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-end-0 { - border-right: 0 !important; + border-right: 0 !important; } .border-bottom { - border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-bottom-0 { - border-bottom: 0 !important; + border-bottom: 0 !important; } .border-start { - border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-start-0 { - border-left: 0 !important; + border-left: 0 !important; } .border-primary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; } .border-secondary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; } .border-success { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; } .border-info { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; } .border-warning { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; } .border-danger { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; } .border-light { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; } .border-dark { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } .border-black { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; } .border-white { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } .border-primary-subtle { - border-color: var(--bs-primary-border-subtle) !important; + border-color: var(--bs-primary-border-subtle) !important; } .border-secondary-subtle { - border-color: var(--bs-secondary-border-subtle) !important; + border-color: var(--bs-secondary-border-subtle) !important; } .border-success-subtle { - border-color: var(--bs-success-border-subtle) !important; + border-color: var(--bs-success-border-subtle) !important; } .border-info-subtle { - border-color: var(--bs-info-border-subtle) !important; + border-color: var(--bs-info-border-subtle) !important; } .border-warning-subtle { - border-color: var(--bs-warning-border-subtle) !important; + border-color: var(--bs-warning-border-subtle) !important; } .border-danger-subtle { - border-color: var(--bs-danger-border-subtle) !important; + border-color: var(--bs-danger-border-subtle) !important; } .border-light-subtle { - border-color: var(--bs-light-border-subtle) !important; + border-color: var(--bs-light-border-subtle) !important; } .border-dark-subtle { - border-color: var(--bs-dark-border-subtle) !important; + border-color: var(--bs-dark-border-subtle) !important; } .border-1 { - border-width: 1px !important; + border-width: 1px !important; } .border-2 { - border-width: 2px !important; + border-width: 2px !important; } .border-3 { - border-width: 3px !important; + border-width: 3px !important; } .border-4 { - border-width: 4px !important; + border-width: 4px !important; } .border-5 { - border-width: 5px !important; + border-width: 5px !important; } .border-opacity-10 { - --bs-border-opacity: 0.1; + --bs-border-opacity: 0.1; } .border-opacity-25 { - --bs-border-opacity: 0.25; + --bs-border-opacity: 0.25; } .border-opacity-50 { - --bs-border-opacity: 0.5; + --bs-border-opacity: 0.5; } .border-opacity-75 { - --bs-border-opacity: 0.75; + --bs-border-opacity: 0.75; } .border-opacity-100 { - --bs-border-opacity: 1; + --bs-border-opacity: 1; } .w-25 { - width: 25% !important; + width: 25% !important; } .w-50 { - width: 50% !important; + width: 50% !important; } .w-75 { - width: 75% !important; + width: 75% !important; } .w-100 { - width: 100% !important; + width: 100% !important; } .w-auto { - width: auto !important; + width: auto !important; } .mw-100 { - max-width: 100% !important; + max-width: 100% !important; } .vw-100 { - width: 100vw !important; + width: 100vw !important; } .min-vw-100 { - min-width: 100vw !important; + min-width: 100vw !important; } .h-25 { - height: 25% !important; + height: 25% !important; } .h-50 { - height: 50% !important; + height: 50% !important; } .h-75 { - height: 75% !important; + height: 75% !important; } .h-100 { - height: 100% !important; + height: 100% !important; } .h-auto { - height: auto !important; + height: auto !important; } .mh-100 { - max-height: 100% !important; + max-height: 100% !important; } .vh-100 { - height: 100vh !important; + height: 100vh !important; } .min-vh-100 { - min-height: 100vh !important; + min-height: 100vh !important; } .flex-fill { - flex: 1 1 auto !important; + flex: 1 1 auto !important; } .flex-row { - flex-direction: row !important; + flex-direction: row !important; } .flex-column { - flex-direction: column !important; + flex-direction: column !important; } .flex-row-reverse { - flex-direction: row-reverse !important; + flex-direction: row-reverse !important; } .flex-column-reverse { - flex-direction: column-reverse !important; + flex-direction: column-reverse !important; } .flex-grow-0 { - flex-grow: 0 !important; + flex-grow: 0 !important; } .flex-grow-1 { - flex-grow: 1 !important; + flex-grow: 1 !important; } .flex-shrink-0 { - flex-shrink: 0 !important; + flex-shrink: 0 !important; } .flex-shrink-1 { - flex-shrink: 1 !important; + flex-shrink: 1 !important; } .flex-wrap { - flex-wrap: wrap !important; + flex-wrap: wrap !important; } .flex-nowrap { - flex-wrap: nowrap !important; + flex-wrap: nowrap !important; } .flex-wrap-reverse { - flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; } .justify-content-start { - justify-content: flex-start !important; + justify-content: flex-start !important; } .justify-content-end { - justify-content: flex-end !important; + justify-content: flex-end !important; } .justify-content-center { - justify-content: center !important; + justify-content: center !important; } .justify-content-between { - justify-content: space-between !important; + justify-content: space-between !important; } .justify-content-around { - justify-content: space-around !important; + justify-content: space-around !important; } .justify-content-evenly { - justify-content: space-evenly !important; + justify-content: space-evenly !important; } .align-items-start { - align-items: flex-start !important; + align-items: flex-start !important; } .align-items-end { - align-items: flex-end !important; + align-items: flex-end !important; } .align-items-center { - align-items: center !important; + align-items: center !important; } .align-items-baseline { - align-items: baseline !important; + align-items: baseline !important; } .align-items-stretch { - align-items: stretch !important; + align-items: stretch !important; } .align-content-start { - align-content: flex-start !important; + align-content: flex-start !important; } .align-content-end { - align-content: flex-end !important; + align-content: flex-end !important; } .align-content-center { - align-content: center !important; + align-content: center !important; } .align-content-between { - align-content: space-between !important; + align-content: space-between !important; } .align-content-around { - align-content: space-around !important; + align-content: space-around !important; } .align-content-stretch { - align-content: stretch !important; + align-content: stretch !important; } .align-self-auto { - align-self: auto !important; + align-self: auto !important; } .align-self-start { - align-self: flex-start !important; + align-self: flex-start !important; } .align-self-end { - align-self: flex-end !important; + align-self: flex-end !important; } .align-self-center { - align-self: center !important; + align-self: center !important; } .align-self-baseline { - align-self: baseline !important; + align-self: baseline !important; } .align-self-stretch { - align-self: stretch !important; + align-self: stretch !important; } .order-first { - order: -1 !important; + order: -1 !important; } .order-0 { - order: 0 !important; + order: 0 !important; } .order-1 { - order: 1 !important; + order: 1 !important; } .order-2 { - order: 2 !important; + order: 2 !important; } .order-3 { - order: 3 !important; + order: 3 !important; } .order-4 { - order: 4 !important; + order: 4 !important; } .order-5 { - order: 5 !important; + order: 5 !important; } .order-last { - order: 6 !important; + order: 6 !important; } .m-0 { - margin: 0 !important; + margin: 0 !important; } .m-1 { - margin: 0.25rem !important; + margin: 0.25rem !important; } .m-2 { - margin: 0.5rem !important; + margin: 0.5rem !important; } .m-3 { - margin: 1rem !important; + margin: 1rem !important; } .m-4 { - margin: 1.5rem !important; + margin: 1.5rem !important; } .m-5 { - margin: 3rem !important; + margin: 3rem !important; } .m-auto { - margin: auto !important; + margin: auto !important; } .mx-0 { - margin-right: 0 !important; - margin-left: 0 !important; + margin-right: 0 !important; + margin-left: 0 !important; } .mx-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } .mx-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } .mx-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; + margin-right: 1rem !important; + margin-left: 1rem !important; } .mx-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } .mx-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; + margin-right: 3rem !important; + margin-left: 3rem !important; } .mx-auto { - margin-right: auto !important; - margin-left: auto !important; + margin-right: auto !important; + margin-left: auto !important; } .my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; } .my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } .my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } .my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; + margin-top: 1rem !important; + margin-bottom: 1rem !important; } .my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } .my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; + margin-top: 3rem !important; + margin-bottom: 3rem !important; } .my-auto { - margin-top: auto !important; - margin-bottom: auto !important; + margin-top: auto !important; + margin-bottom: auto !important; } .mt-0 { - margin-top: 0 !important; + margin-top: 0 !important; } .mt-1 { - margin-top: 0.25rem !important; + margin-top: 0.25rem !important; } .mt-2 { - margin-top: 0.5rem !important; + margin-top: 0.5rem !important; } .mt-3 { - margin-top: 1rem !important; + margin-top: 1rem !important; } .mt-4 { - margin-top: 1.5rem !important; + margin-top: 1.5rem !important; } .mt-5 { - margin-top: 3rem !important; + margin-top: 3rem !important; } .mt-auto { - margin-top: auto !important; + margin-top: auto !important; } .me-0 { - margin-right: 0 !important; + margin-right: 0 !important; } .me-1 { - margin-right: 0.25rem !important; + margin-right: 0.25rem !important; } .me-2 { - margin-right: 0.5rem !important; + margin-right: 0.5rem !important; } .me-3 { - margin-right: 1rem !important; + margin-right: 1rem !important; } .me-4 { - margin-right: 1.5rem !important; + margin-right: 1.5rem !important; } .me-5 { - margin-right: 3rem !important; + margin-right: 3rem !important; } .me-auto { - margin-right: auto !important; + margin-right: auto !important; } .mb-0 { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .mb-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 0.25rem !important; } .mb-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 0.5rem !important; } .mb-3 { - margin-bottom: 1rem !important; + margin-bottom: 1rem !important; } .mb-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 1.5rem !important; } .mb-5 { - margin-bottom: 3rem !important; + margin-bottom: 3rem !important; } .mb-auto { - margin-bottom: auto !important; + margin-bottom: auto !important; } .ms-0 { - margin-left: 0 !important; + margin-left: 0 !important; } .ms-1 { - margin-left: 0.25rem !important; + margin-left: 0.25rem !important; } .ms-2 { - margin-left: 0.5rem !important; + margin-left: 0.5rem !important; } .ms-3 { - margin-left: 1rem !important; + margin-left: 1rem !important; } .ms-4 { - margin-left: 1.5rem !important; + margin-left: 1.5rem !important; } .ms-5 { - margin-left: 3rem !important; + margin-left: 3rem !important; } .ms-auto { - margin-left: auto !important; + margin-left: auto !important; } .p-0 { - padding: 0 !important; + padding: 0 !important; } .p-1 { - padding: 0.25rem !important; + padding: 0.25rem !important; } .p-2 { - padding: 0.5rem !important; + padding: 0.5rem !important; } .p-3 { - padding: 1rem !important; + padding: 1rem !important; } .p-4 { - padding: 1.5rem !important; + padding: 1.5rem !important; } .p-5 { - padding: 3rem !important; + padding: 3rem !important; } .px-0 { - padding-right: 0 !important; - padding-left: 0 !important; + padding-right: 0 !important; + padding-left: 0 !important; } .px-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } .px-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } .px-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; + padding-right: 1rem !important; + padding-left: 1rem !important; } .px-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } .px-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; + padding-right: 3rem !important; + padding-left: 3rem !important; } .py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; } .py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } .py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } .py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; + padding-top: 1rem !important; + padding-bottom: 1rem !important; } .py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } .py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; + padding-top: 3rem !important; + padding-bottom: 3rem !important; } .pt-0 { - padding-top: 0 !important; + padding-top: 0 !important; } .pt-1 { - padding-top: 0.25rem !important; + padding-top: 0.25rem !important; } .pt-2 { - padding-top: 0.5rem !important; + padding-top: 0.5rem !important; } .pt-3 { - padding-top: 1rem !important; + padding-top: 1rem !important; } .pt-4 { - padding-top: 1.5rem !important; + padding-top: 1.5rem !important; } .pt-5 { - padding-top: 3rem !important; + padding-top: 3rem !important; } .pe-0 { - padding-right: 0 !important; + padding-right: 0 !important; } .pe-1 { - padding-right: 0.25rem !important; + padding-right: 0.25rem !important; } .pe-2 { - padding-right: 0.5rem !important; + padding-right: 0.5rem !important; } .pe-3 { - padding-right: 1rem !important; + padding-right: 1rem !important; } .pe-4 { - padding-right: 1.5rem !important; + padding-right: 1.5rem !important; } .pe-5 { - padding-right: 3rem !important; + padding-right: 3rem !important; } .pb-0 { - padding-bottom: 0 !important; + padding-bottom: 0 !important; } .pb-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 0.25rem !important; } .pb-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 0.5rem !important; } .pb-3 { - padding-bottom: 1rem !important; + padding-bottom: 1rem !important; } .pb-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 1.5rem !important; } .pb-5 { - padding-bottom: 3rem !important; + padding-bottom: 3rem !important; } .ps-0 { - padding-left: 0 !important; + padding-left: 0 !important; } .ps-1 { - padding-left: 0.25rem !important; + padding-left: 0.25rem !important; } .ps-2 { - padding-left: 0.5rem !important; + padding-left: 0.5rem !important; } .ps-3 { - padding-left: 1rem !important; + padding-left: 1rem !important; } .ps-4 { - padding-left: 1.5rem !important; + padding-left: 1.5rem !important; } .ps-5 { - padding-left: 3rem !important; + padding-left: 3rem !important; } .gap-0 { - gap: 0 !important; + gap: 0 !important; } .gap-1 { - gap: 0.25rem !important; + gap: 0.25rem !important; } .gap-2 { - gap: 0.5rem !important; + gap: 0.5rem !important; } .gap-3 { - gap: 1rem !important; + gap: 1rem !important; } .gap-4 { - gap: 1.5rem !important; + gap: 1.5rem !important; } .gap-5 { - gap: 3rem !important; + gap: 3rem !important; } .row-gap-0 { - row-gap: 0 !important; + row-gap: 0 !important; } .row-gap-1 { - row-gap: 0.25rem !important; + row-gap: 0.25rem !important; } .row-gap-2 { - row-gap: 0.5rem !important; + row-gap: 0.5rem !important; } .row-gap-3 { - row-gap: 1rem !important; + row-gap: 1rem !important; } .row-gap-4 { - row-gap: 1.5rem !important; + row-gap: 1.5rem !important; } .row-gap-5 { - row-gap: 3rem !important; + row-gap: 3rem !important; } .column-gap-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; + -moz-column-gap: 0 !important; + column-gap: 0 !important; } .column-gap-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; } .column-gap-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; } .column-gap-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; } .column-gap-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; } .column-gap-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; } .font-monospace { - font-family: var(--bs-font-monospace) !important; + font-family: var(--bs-font-monospace) !important; } .fs-1 { - font-size: calc(1.375rem + 1.5vw) !important; + font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { - font-size: calc(1.325rem + 0.9vw) !important; + font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { - font-size: calc(1.3rem + 0.6vw) !important; + font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { - font-size: calc(1.275rem + 0.3vw) !important; + font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { - font-size: 1.25rem !important; + font-size: 1.25rem !important; } .fs-6 { - font-size: 1rem !important; + font-size: 1rem !important; } .fst-italic { - font-style: italic !important; + font-style: italic !important; } .fst-normal { - font-style: normal !important; + font-style: normal !important; } .fw-lighter { - font-weight: lighter !important; + font-weight: lighter !important; } .fw-light { - font-weight: 300 !important; + font-weight: 300 !important; } .fw-normal { - font-weight: 400 !important; + font-weight: 400 !important; } .fw-medium { - font-weight: 500 !important; + font-weight: 500 !important; } .fw-semibold { - font-weight: 600 !important; + font-weight: 600 !important; } .fw-bold { - font-weight: 700 !important; + font-weight: 700 !important; } .fw-bolder { - font-weight: bolder !important; + font-weight: bolder !important; } .lh-1 { - line-height: 1 !important; + line-height: 1 !important; } .lh-sm { - line-height: 1.25 !important; + line-height: 1.25 !important; } .lh-base { - line-height: 1.5 !important; + line-height: 1.5 !important; } .lh-lg { - line-height: 2 !important; + line-height: 2 !important; } .text-start { - text-align: left !important; + text-align: left !important; } .text-end { - text-align: right !important; + text-align: right !important; } .text-center { - text-align: center !important; + text-align: center !important; } .text-decoration-none { - text-decoration: none !important; + text-decoration: none !important; } .text-decoration-underline { - text-decoration: underline !important; + text-decoration: underline !important; } .text-decoration-line-through { - text-decoration: line-through !important; + text-decoration: line-through !important; } .text-lowercase { - text-transform: lowercase !important; + text-transform: lowercase !important; } .text-uppercase { - text-transform: uppercase !important; + text-transform: uppercase !important; } .text-capitalize { - text-transform: capitalize !important; + text-transform: capitalize !important; } .text-wrap { - white-space: normal !important; + white-space: normal !important; } .text-nowrap { - white-space: nowrap !important; + white-space: nowrap !important; } /* rtl:begin:remove */ .text-break { - word-wrap: break-word !important; - word-break: break-word !important; + word-wrap: break-word !important; + word-break: break-word !important; } /* rtl:end:remove */ .text-primary { - --bs-text-opacity: 1; - color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { - --bs-text-opacity: 1; - color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { - --bs-text-opacity: 1; - color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { - --bs-text-opacity: 1; - color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { - --bs-text-opacity: 1; - color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { - --bs-text-opacity: 1; - color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { - --bs-text-opacity: 1; - color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { - --bs-text-opacity: 1; - color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { - --bs-text-opacity: 1; - color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { - --bs-text-opacity: 1; - color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { - --bs-text-opacity: 1; - color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-black-50 { - --bs-text-opacity: 1; - color: rgba(0, 0, 0, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { - --bs-text-opacity: 1; - color: rgba(255, 255, 255, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(255, 255, 255, 0.5) !important; } .text-body-secondary { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-body-tertiary { - --bs-text-opacity: 1; - color: var(--bs-tertiary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-tertiary-color) !important; } .text-body-emphasis { - --bs-text-opacity: 1; - color: var(--bs-emphasis-color) !important; + --bs-text-opacity: 1; + color: var(--bs-emphasis-color) !important; } .text-reset { - --bs-text-opacity: 1; - color: inherit !important; + --bs-text-opacity: 1; + color: inherit !important; } .text-opacity-25 { - --bs-text-opacity: 0.25; + --bs-text-opacity: 0.25; } .text-opacity-50 { - --bs-text-opacity: 0.5; + --bs-text-opacity: 0.5; } .text-opacity-75 { - --bs-text-opacity: 0.75; + --bs-text-opacity: 0.75; } .text-opacity-100 { - --bs-text-opacity: 1; + --bs-text-opacity: 1; } .text-primary-emphasis { - color: var(--bs-primary-text-emphasis) !important; + color: var(--bs-primary-text-emphasis) !important; } .text-secondary-emphasis { - color: var(--bs-secondary-text-emphasis) !important; + color: var(--bs-secondary-text-emphasis) !important; } .text-success-emphasis { - color: var(--bs-success-text-emphasis) !important; + color: var(--bs-success-text-emphasis) !important; } .text-info-emphasis { - color: var(--bs-info-text-emphasis) !important; + color: var(--bs-info-text-emphasis) !important; } .text-warning-emphasis { - color: var(--bs-warning-text-emphasis) !important; + color: var(--bs-warning-text-emphasis) !important; } .text-danger-emphasis { - color: var(--bs-danger-text-emphasis) !important; + color: var(--bs-danger-text-emphasis) !important; } .text-light-emphasis { - color: var(--bs-light-text-emphasis) !important; + color: var(--bs-light-text-emphasis) !important; } .text-dark-emphasis { - color: var(--bs-dark-text-emphasis) !important; + color: var(--bs-dark-text-emphasis) !important; } .link-opacity-10 { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-10-hover:hover { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-25 { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-25-hover:hover { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-50 { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-50-hover:hover { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-75 { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-75-hover:hover { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-100 { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-opacity-100-hover:hover { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-offset-1 { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-1-hover:hover { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-2 { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-2-hover:hover { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-3 { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-offset-3-hover:hover { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-underline-primary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-secondary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-success { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-info { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-warning { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-danger { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-light { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-dark { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-underline-opacity-0 { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-0-hover:hover { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-10 { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-10-hover:hover { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-25 { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-25-hover:hover { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-50 { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-50-hover:hover { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-75 { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-75-hover:hover { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-100 { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .link-underline-opacity-100-hover:hover { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .bg-primary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; } .bg-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; } .bg-success { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; } .bg-info { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; } .bg-danger { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-transparent { - --bs-bg-opacity: 1; - background-color: transparent !important; + --bs-bg-opacity: 1; + background-color: transparent !important; } .bg-body-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-body-tertiary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-opacity-10 { - --bs-bg-opacity: 0.1; + --bs-bg-opacity: 0.1; } .bg-opacity-25 { - --bs-bg-opacity: 0.25; + --bs-bg-opacity: 0.25; } .bg-opacity-50 { - --bs-bg-opacity: 0.5; + --bs-bg-opacity: 0.5; } .bg-opacity-75 { - --bs-bg-opacity: 0.75; + --bs-bg-opacity: 0.75; } .bg-opacity-100 { - --bs-bg-opacity: 1; + --bs-bg-opacity: 1; } .bg-primary-subtle { - background-color: var(--bs-primary-bg-subtle) !important; + background-color: var(--bs-primary-bg-subtle) !important; } .bg-secondary-subtle { - background-color: var(--bs-secondary-bg-subtle) !important; + background-color: var(--bs-secondary-bg-subtle) !important; } .bg-success-subtle { - background-color: var(--bs-success-bg-subtle) !important; + background-color: var(--bs-success-bg-subtle) !important; } .bg-info-subtle { - background-color: var(--bs-info-bg-subtle) !important; + background-color: var(--bs-info-bg-subtle) !important; } .bg-warning-subtle { - background-color: var(--bs-warning-bg-subtle) !important; + background-color: var(--bs-warning-bg-subtle) !important; } .bg-danger-subtle { - background-color: var(--bs-danger-bg-subtle) !important; + background-color: var(--bs-danger-bg-subtle) !important; } .bg-light-subtle { - background-color: var(--bs-light-bg-subtle) !important; + background-color: var(--bs-light-bg-subtle) !important; } .bg-dark-subtle { - background-color: var(--bs-dark-bg-subtle) !important; + background-color: var(--bs-dark-bg-subtle) !important; } .bg-gradient { - background-image: var(--bs-gradient) !important; + background-image: var(--bs-gradient) !important; } .user-select-all { - -webkit-user-select: all !important; - -moz-user-select: all !important; - user-select: all !important; + -webkit-user-select: all !important; + -moz-user-select: all !important; + user-select: all !important; } .user-select-auto { - -webkit-user-select: auto !important; - -moz-user-select: auto !important; - user-select: auto !important; + -webkit-user-select: auto !important; + -moz-user-select: auto !important; + user-select: auto !important; } .user-select-none { - -webkit-user-select: none !important; - -moz-user-select: none !important; - user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + user-select: none !important; } .pe-none { - pointer-events: none !important; + pointer-events: none !important; } .pe-auto { - pointer-events: auto !important; + pointer-events: auto !important; } .rounded { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-0 { - border-radius: 0 !important; + border-radius: 0 !important; } .rounded-1 { - border-radius: var(--bs-border-radius-sm) !important; + border-radius: var(--bs-border-radius-sm) !important; } .rounded-2 { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-3 { - border-radius: var(--bs-border-radius-lg) !important; + border-radius: var(--bs-border-radius-lg) !important; } .rounded-4 { - border-radius: var(--bs-border-radius-xl) !important; + border-radius: var(--bs-border-radius-xl) !important; } .rounded-5 { - border-radius: var(--bs-border-radius-xxl) !important; + border-radius: var(--bs-border-radius-xxl) !important; } .rounded-circle { - border-radius: 50% !important; + border-radius: 50% !important; } .rounded-pill { - border-radius: var(--bs-border-radius-pill) !important; + border-radius: var(--bs-border-radius-pill) !important; } .rounded-top { - border-top-left-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-top-0 { - border-top-left-radius: 0 !important; - border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; } .rounded-top-1 { - border-top-left-radius: var(--bs-border-radius-sm) !important; - border-top-right-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; } .rounded-top-2 { - border-top-left-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-top-3 { - border-top-left-radius: var(--bs-border-radius-lg) !important; - border-top-right-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; } .rounded-top-4 { - border-top-left-radius: var(--bs-border-radius-xl) !important; - border-top-right-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; } .rounded-top-5 { - border-top-left-radius: var(--bs-border-radius-xxl) !important; - border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-top-circle { - border-top-left-radius: 50% !important; - border-top-right-radius: 50% !important; + border-top-left-radius: 50% !important; + border-top-right-radius: 50% !important; } .rounded-top-pill { - border-top-left-radius: var(--bs-border-radius-pill) !important; - border-top-right-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; } .rounded-end { - border-top-right-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-end-0 { - border-top-right-radius: 0 !important; - border-bottom-right-radius: 0 !important; + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; } .rounded-end-1 { - border-top-right-radius: var(--bs-border-radius-sm) !important; - border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; } .rounded-end-2 { - border-top-right-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-end-3 { - border-top-right-radius: var(--bs-border-radius-lg) !important; - border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; } .rounded-end-4 { - border-top-right-radius: var(--bs-border-radius-xl) !important; - border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; } .rounded-end-5 { - border-top-right-radius: var(--bs-border-radius-xxl) !important; - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-end-circle { - border-top-right-radius: 50% !important; - border-bottom-right-radius: 50% !important; + border-top-right-radius: 50% !important; + border-bottom-right-radius: 50% !important; } .rounded-end-pill { - border-top-right-radius: var(--bs-border-radius-pill) !important; - border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; } .rounded-bottom { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-bottom-0 { - border-bottom-right-radius: 0 !important; - border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important; } .rounded-bottom-1 { - border-bottom-right-radius: var(--bs-border-radius-sm) !important; - border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; } .rounded-bottom-2 { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-bottom-3 { - border-bottom-right-radius: var(--bs-border-radius-lg) !important; - border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; } .rounded-bottom-4 { - border-bottom-right-radius: var(--bs-border-radius-xl) !important; - border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; } .rounded-bottom-5 { - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-bottom-circle { - border-bottom-right-radius: 50% !important; - border-bottom-left-radius: 50% !important; + border-bottom-right-radius: 50% !important; + border-bottom-left-radius: 50% !important; } .rounded-bottom-pill { - border-bottom-right-radius: var(--bs-border-radius-pill) !important; - border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; } .rounded-start { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-start-0 { - border-bottom-left-radius: 0 !important; - border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important; } .rounded-start-1 { - border-bottom-left-radius: var(--bs-border-radius-sm) !important; - border-top-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; } .rounded-start-2 { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-start-3 { - border-bottom-left-radius: var(--bs-border-radius-lg) !important; - border-top-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; } .rounded-start-4 { - border-bottom-left-radius: var(--bs-border-radius-xl) !important; - border-top-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; } .rounded-start-5 { - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; - border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-start-circle { - border-bottom-left-radius: 50% !important; - border-top-left-radius: 50% !important; + border-bottom-left-radius: 50% !important; + border-top-left-radius: 50% !important; } .rounded-start-pill { - border-bottom-left-radius: var(--bs-border-radius-pill) !important; - border-top-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; } .visible { - visibility: visible !important; + visibility: visible !important; } .invisible { - visibility: hidden !important; + visibility: hidden !important; } .z-n1 { - z-index: -1 !important; + z-index: -1 !important; } .z-0 { - z-index: 0 !important; + z-index: 0 !important; } .z-1 { - z-index: 1 !important; + z-index: 1 !important; } .z-2 { - z-index: 2 !important; + z-index: 2 !important; } .z-3 { - z-index: 3 !important; + z-index: 3 !important; } @media (min-width: 576px) { - .float-sm-start { - float: left !important; - } - .float-sm-end { - float: right !important; - } - .float-sm-none { - float: none !important; - } - .object-fit-sm-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-sm-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-sm-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-sm-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-sm-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-sm-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-sm-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-sm-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-sm-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-sm-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-sm-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-right: 0 !important; - } - .me-sm-1 { - margin-right: 0.25rem !important; - } - .me-sm-2 { - margin-right: 0.5rem !important; - } - .me-sm-3 { - margin-right: 1rem !important; - } - .me-sm-4 { - margin-right: 1.5rem !important; - } - .me-sm-5 { - margin-right: 3rem !important; - } - .me-sm-auto { - margin-right: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-left: 0 !important; - } - .ms-sm-1 { - margin-left: 0.25rem !important; - } - .ms-sm-2 { - margin-left: 0.5rem !important; - } - .ms-sm-3 { - margin-left: 1rem !important; - } - .ms-sm-4 { - margin-left: 1.5rem !important; - } - .ms-sm-5 { - margin-left: 3rem !important; - } - .ms-sm-auto { - margin-left: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-sm-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-sm-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-sm-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-sm-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-sm-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-right: 0 !important; - } - .pe-sm-1 { - padding-right: 0.25rem !important; - } - .pe-sm-2 { - padding-right: 0.5rem !important; - } - .pe-sm-3 { - padding-right: 1rem !important; - } - .pe-sm-4 { - padding-right: 1.5rem !important; - } - .pe-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-left: 0 !important; - } - .ps-sm-1 { - padding-left: 0.25rem !important; - } - .ps-sm-2 { - padding-left: 0.5rem !important; - } - .ps-sm-3 { - padding-left: 1rem !important; - } - .ps-sm-4 { - padding-left: 1.5rem !important; - } - .ps-sm-5 { - padding-left: 3rem !important; - } - .gap-sm-0 { - gap: 0 !important; - } - .gap-sm-1 { - gap: 0.25rem !important; - } - .gap-sm-2 { - gap: 0.5rem !important; - } - .gap-sm-3 { - gap: 1rem !important; - } - .gap-sm-4 { - gap: 1.5rem !important; - } - .gap-sm-5 { - gap: 3rem !important; - } - .row-gap-sm-0 { - row-gap: 0 !important; - } - .row-gap-sm-1 { - row-gap: 0.25rem !important; - } - .row-gap-sm-2 { - row-gap: 0.5rem !important; - } - .row-gap-sm-3 { - row-gap: 1rem !important; - } - .row-gap-sm-4 { - row-gap: 1.5rem !important; - } - .row-gap-sm-5 { - row-gap: 3rem !important; - } - .column-gap-sm-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-sm-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-sm-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-sm-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-sm-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-sm-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-sm-start { - text-align: left !important; - } - .text-sm-end { - text-align: right !important; - } - .text-sm-center { - text-align: center !important; - } -} -@media (min-width: 768px) { - .float-md-start { - float: left !important; - } - .float-md-end { - float: right !important; - } - .float-md-none { - float: none !important; - } - .object-fit-md-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-md-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-md-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-md-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-md-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-md-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-md-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-md-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-md-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-md-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-md-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-right: 0 !important; - } - .me-md-1 { - margin-right: 0.25rem !important; - } - .me-md-2 { - margin-right: 0.5rem !important; - } - .me-md-3 { - margin-right: 1rem !important; - } - .me-md-4 { - margin-right: 1.5rem !important; - } - .me-md-5 { - margin-right: 3rem !important; - } - .me-md-auto { - margin-right: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-left: 0 !important; - } - .ms-md-1 { - margin-left: 0.25rem !important; - } - .ms-md-2 { - margin-left: 0.5rem !important; - } - .ms-md-3 { - margin-left: 1rem !important; - } - .ms-md-4 { - margin-left: 1.5rem !important; - } - .ms-md-5 { - margin-left: 3rem !important; - } - .ms-md-auto { - margin-left: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-md-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-md-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-md-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-md-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-md-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-right: 0 !important; - } - .pe-md-1 { - padding-right: 0.25rem !important; - } - .pe-md-2 { - padding-right: 0.5rem !important; - } - .pe-md-3 { - padding-right: 1rem !important; - } - .pe-md-4 { - padding-right: 1.5rem !important; - } - .pe-md-5 { - padding-right: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-left: 0 !important; - } - .ps-md-1 { - padding-left: 0.25rem !important; - } - .ps-md-2 { - padding-left: 0.5rem !important; - } - .ps-md-3 { - padding-left: 1rem !important; - } - .ps-md-4 { - padding-left: 1.5rem !important; - } - .ps-md-5 { - padding-left: 3rem !important; - } - .gap-md-0 { - gap: 0 !important; - } - .gap-md-1 { - gap: 0.25rem !important; - } - .gap-md-2 { - gap: 0.5rem !important; - } - .gap-md-3 { - gap: 1rem !important; - } - .gap-md-4 { - gap: 1.5rem !important; - } - .gap-md-5 { - gap: 3rem !important; - } - .row-gap-md-0 { - row-gap: 0 !important; - } - .row-gap-md-1 { - row-gap: 0.25rem !important; - } - .row-gap-md-2 { - row-gap: 0.5rem !important; - } - .row-gap-md-3 { - row-gap: 1rem !important; - } - .row-gap-md-4 { - row-gap: 1.5rem !important; - } - .row-gap-md-5 { - row-gap: 3rem !important; - } - .column-gap-md-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-md-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-md-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-md-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-md-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-md-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-md-start { - text-align: left !important; - } - .text-md-end { - text-align: right !important; - } - .text-md-center { - text-align: center !important; - } -} -@media (min-width: 992px) { - .float-lg-start { - float: left !important; - } - .float-lg-end { - float: right !important; - } - .float-lg-none { - float: none !important; - } - .object-fit-lg-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-lg-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-lg-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-lg-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-lg-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-lg-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-lg-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-lg-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-lg-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-lg-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-lg-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-right: 0 !important; - } - .me-lg-1 { - margin-right: 0.25rem !important; - } - .me-lg-2 { - margin-right: 0.5rem !important; - } - .me-lg-3 { - margin-right: 1rem !important; - } - .me-lg-4 { - margin-right: 1.5rem !important; - } - .me-lg-5 { - margin-right: 3rem !important; - } - .me-lg-auto { - margin-right: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-left: 0 !important; - } - .ms-lg-1 { - margin-left: 0.25rem !important; - } - .ms-lg-2 { - margin-left: 0.5rem !important; - } - .ms-lg-3 { - margin-left: 1rem !important; - } - .ms-lg-4 { - margin-left: 1.5rem !important; - } - .ms-lg-5 { - margin-left: 3rem !important; - } - .ms-lg-auto { - margin-left: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-lg-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-lg-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-lg-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-lg-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-lg-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-right: 0 !important; - } - .pe-lg-1 { - padding-right: 0.25rem !important; - } - .pe-lg-2 { - padding-right: 0.5rem !important; - } - .pe-lg-3 { - padding-right: 1rem !important; - } - .pe-lg-4 { - padding-right: 1.5rem !important; - } - .pe-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-left: 0 !important; - } - .ps-lg-1 { - padding-left: 0.25rem !important; - } - .ps-lg-2 { - padding-left: 0.5rem !important; - } - .ps-lg-3 { - padding-left: 1rem !important; - } - .ps-lg-4 { - padding-left: 1.5rem !important; - } - .ps-lg-5 { - padding-left: 3rem !important; - } - .gap-lg-0 { - gap: 0 !important; - } - .gap-lg-1 { - gap: 0.25rem !important; - } - .gap-lg-2 { - gap: 0.5rem !important; - } - .gap-lg-3 { - gap: 1rem !important; - } - .gap-lg-4 { - gap: 1.5rem !important; - } - .gap-lg-5 { - gap: 3rem !important; - } - .row-gap-lg-0 { - row-gap: 0 !important; - } - .row-gap-lg-1 { - row-gap: 0.25rem !important; - } - .row-gap-lg-2 { - row-gap: 0.5rem !important; - } - .row-gap-lg-3 { - row-gap: 1rem !important; - } - .row-gap-lg-4 { - row-gap: 1.5rem !important; - } - .row-gap-lg-5 { - row-gap: 3rem !important; - } - .column-gap-lg-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-lg-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-lg-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-lg-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-lg-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-lg-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-lg-start { - text-align: left !important; - } - .text-lg-end { - text-align: right !important; - } - .text-lg-center { - text-align: center !important; - } -} -@media (min-width: 1200px) { - .float-xl-start { - float: left !important; - } - .float-xl-end { - float: right !important; - } - .float-xl-none { - float: none !important; - } - .object-fit-xl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-right: 0 !important; - } - .me-xl-1 { - margin-right: 0.25rem !important; - } - .me-xl-2 { - margin-right: 0.5rem !important; - } - .me-xl-3 { - margin-right: 1rem !important; - } - .me-xl-4 { - margin-right: 1.5rem !important; - } - .me-xl-5 { - margin-right: 3rem !important; - } - .me-xl-auto { - margin-right: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-left: 0 !important; - } - .ms-xl-1 { - margin-left: 0.25rem !important; - } - .ms-xl-2 { - margin-left: 0.5rem !important; - } - .ms-xl-3 { - margin-left: 1rem !important; - } - .ms-xl-4 { - margin-left: 1.5rem !important; - } - .ms-xl-5 { - margin-left: 3rem !important; - } - .ms-xl-auto { - margin-left: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-right: 0 !important; - } - .pe-xl-1 { - padding-right: 0.25rem !important; - } - .pe-xl-2 { - padding-right: 0.5rem !important; - } - .pe-xl-3 { - padding-right: 1rem !important; - } - .pe-xl-4 { - padding-right: 1.5rem !important; - } - .pe-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-left: 0 !important; - } - .ps-xl-1 { - padding-left: 0.25rem !important; - } - .ps-xl-2 { - padding-left: 0.5rem !important; - } - .ps-xl-3 { - padding-left: 1rem !important; - } - .ps-xl-4 { - padding-left: 1.5rem !important; - } - .ps-xl-5 { - padding-left: 3rem !important; - } - .gap-xl-0 { - gap: 0 !important; - } - .gap-xl-1 { - gap: 0.25rem !important; - } - .gap-xl-2 { - gap: 0.5rem !important; - } - .gap-xl-3 { - gap: 1rem !important; - } - .gap-xl-4 { - gap: 1.5rem !important; - } - .gap-xl-5 { - gap: 3rem !important; - } - .row-gap-xl-0 { - row-gap: 0 !important; - } - .row-gap-xl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xl-3 { - row-gap: 1rem !important; - } - .row-gap-xl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xl-5 { - row-gap: 3rem !important; - } - .column-gap-xl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xl-start { - text-align: left !important; - } - .text-xl-end { - text-align: right !important; - } - .text-xl-center { - text-align: center !important; - } -} -@media (min-width: 1400px) { - .float-xxl-start { - float: left !important; - } - .float-xxl-end { - float: right !important; - } - .float-xxl-none { - float: none !important; - } - .object-fit-xxl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xxl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xxl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xxl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xxl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xxl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xxl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xxl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xxl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xxl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xxl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-right: 0 !important; - } - .me-xxl-1 { - margin-right: 0.25rem !important; - } - .me-xxl-2 { - margin-right: 0.5rem !important; - } - .me-xxl-3 { - margin-right: 1rem !important; - } - .me-xxl-4 { - margin-right: 1.5rem !important; - } - .me-xxl-5 { - margin-right: 3rem !important; - } - .me-xxl-auto { - margin-right: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-left: 0 !important; - } - .ms-xxl-1 { - margin-left: 0.25rem !important; - } - .ms-xxl-2 { - margin-left: 0.5rem !important; - } - .ms-xxl-3 { - margin-left: 1rem !important; - } - .ms-xxl-4 { - margin-left: 1.5rem !important; - } - .ms-xxl-5 { - margin-left: 3rem !important; - } - .ms-xxl-auto { - margin-left: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xxl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xxl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xxl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xxl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xxl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-right: 0 !important; - } - .pe-xxl-1 { - padding-right: 0.25rem !important; - } - .pe-xxl-2 { - padding-right: 0.5rem !important; - } - .pe-xxl-3 { - padding-right: 1rem !important; - } - .pe-xxl-4 { - padding-right: 1.5rem !important; - } - .pe-xxl-5 { - padding-right: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-left: 0 !important; - } - .ps-xxl-1 { - padding-left: 0.25rem !important; - } - .ps-xxl-2 { - padding-left: 0.5rem !important; - } - .ps-xxl-3 { - padding-left: 1rem !important; - } - .ps-xxl-4 { - padding-left: 1.5rem !important; - } - .ps-xxl-5 { - padding-left: 3rem !important; - } - .gap-xxl-0 { - gap: 0 !important; - } - .gap-xxl-1 { - gap: 0.25rem !important; - } - .gap-xxl-2 { - gap: 0.5rem !important; - } - .gap-xxl-3 { - gap: 1rem !important; - } - .gap-xxl-4 { - gap: 1.5rem !important; - } - .gap-xxl-5 { - gap: 3rem !important; - } - .row-gap-xxl-0 { - row-gap: 0 !important; - } - .row-gap-xxl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xxl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xxl-3 { - row-gap: 1rem !important; - } - .row-gap-xxl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xxl-5 { - row-gap: 3rem !important; - } - .column-gap-xxl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xxl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xxl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xxl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xxl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xxl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xxl-start { - text-align: left !important; - } - .text-xxl-end { - text-align: right !important; - } - .text-xxl-center { - text-align: center !important; - } + .float-sm-start { + float: left !important; + } + + .float-sm-end { + float: right !important; + } + + .float-sm-none { + float: none !important; + } + + .object-fit-sm-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-sm-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-sm-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-sm-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-sm-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-grid { + display: grid !important; + } + + .d-sm-inline-grid { + display: inline-grid !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: flex !important; + } + + .d-sm-inline-flex { + display: inline-flex !important; + } + + .d-sm-none { + display: none !important; + } + + .flex-sm-fill { + flex: 1 1 auto !important; + } + + .flex-sm-row { + flex-direction: row !important; + } + + .flex-sm-column { + flex-direction: column !important; + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-sm-wrap { + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-sm-start { + justify-content: flex-start !important; + } + + .justify-content-sm-end { + justify-content: flex-end !important; + } + + .justify-content-sm-center { + justify-content: center !important; + } + + .justify-content-sm-between { + justify-content: space-between !important; + } + + .justify-content-sm-around { + justify-content: space-around !important; + } + + .justify-content-sm-evenly { + justify-content: space-evenly !important; + } + + .align-items-sm-start { + align-items: flex-start !important; + } + + .align-items-sm-end { + align-items: flex-end !important; + } + + .align-items-sm-center { + align-items: center !important; + } + + .align-items-sm-baseline { + align-items: baseline !important; + } + + .align-items-sm-stretch { + align-items: stretch !important; + } + + .align-content-sm-start { + align-content: flex-start !important; + } + + .align-content-sm-end { + align-content: flex-end !important; + } + + .align-content-sm-center { + align-content: center !important; + } + + .align-content-sm-between { + align-content: space-between !important; + } + + .align-content-sm-around { + align-content: space-around !important; + } + + .align-content-sm-stretch { + align-content: stretch !important; + } + + .align-self-sm-auto { + align-self: auto !important; + } + + .align-self-sm-start { + align-self: flex-start !important; + } + + .align-self-sm-end { + align-self: flex-end !important; + } + + .align-self-sm-center { + align-self: center !important; + } + + .align-self-sm-baseline { + align-self: baseline !important; + } + + .align-self-sm-stretch { + align-self: stretch !important; + } + + .order-sm-first { + order: -1 !important; + } + + .order-sm-0 { + order: 0 !important; + } + + .order-sm-1 { + order: 1 !important; + } + + .order-sm-2 { + order: 2 !important; + } + + .order-sm-3 { + order: 3 !important; + } + + .order-sm-4 { + order: 4 !important; + } + + .order-sm-5 { + order: 5 !important; + } + + .order-sm-last { + order: 6 !important; + } + + .m-sm-0 { + margin: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mx-sm-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-sm-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-sm-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-sm-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-sm-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-sm-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-sm-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-sm-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-sm-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-sm-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-sm-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-sm-0 { + margin-top: 0 !important; + } + + .mt-sm-1 { + margin-top: 0.25rem !important; + } + + .mt-sm-2 { + margin-top: 0.5rem !important; + } + + .mt-sm-3 { + margin-top: 1rem !important; + } + + .mt-sm-4 { + margin-top: 1.5rem !important; + } + + .mt-sm-5 { + margin-top: 3rem !important; + } + + .mt-sm-auto { + margin-top: auto !important; + } + + .me-sm-0 { + margin-right: 0 !important; + } + + .me-sm-1 { + margin-right: 0.25rem !important; + } + + .me-sm-2 { + margin-right: 0.5rem !important; + } + + .me-sm-3 { + margin-right: 1rem !important; + } + + .me-sm-4 { + margin-right: 1.5rem !important; + } + + .me-sm-5 { + margin-right: 3rem !important; + } + + .me-sm-auto { + margin-right: auto !important; + } + + .mb-sm-0 { + margin-bottom: 0 !important; + } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; + } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; + } + + .mb-sm-3 { + margin-bottom: 1rem !important; + } + + .mb-sm-4 { + margin-bottom: 1.5rem !important; + } + + .mb-sm-5 { + margin-bottom: 3rem !important; + } + + .mb-sm-auto { + margin-bottom: auto !important; + } + + .ms-sm-0 { + margin-left: 0 !important; + } + + .ms-sm-1 { + margin-left: 0.25rem !important; + } + + .ms-sm-2 { + margin-left: 0.5rem !important; + } + + .ms-sm-3 { + margin-left: 1rem !important; + } + + .ms-sm-4 { + margin-left: 1.5rem !important; + } + + .ms-sm-5 { + margin-left: 3rem !important; + } + + .ms-sm-auto { + margin-left: auto !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .px-sm-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-sm-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-sm-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-sm-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-sm-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-sm-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-sm-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-sm-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-sm-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-sm-0 { + padding-top: 0 !important; + } + + .pt-sm-1 { + padding-top: 0.25rem !important; + } + + .pt-sm-2 { + padding-top: 0.5rem !important; + } + + .pt-sm-3 { + padding-top: 1rem !important; + } + + .pt-sm-4 { + padding-top: 1.5rem !important; + } + + .pt-sm-5 { + padding-top: 3rem !important; + } + + .pe-sm-0 { + padding-right: 0 !important; + } + + .pe-sm-1 { + padding-right: 0.25rem !important; + } + + .pe-sm-2 { + padding-right: 0.5rem !important; + } + + .pe-sm-3 { + padding-right: 1rem !important; + } + + .pe-sm-4 { + padding-right: 1.5rem !important; + } + + .pe-sm-5 { + padding-right: 3rem !important; + } + + .pb-sm-0 { + padding-bottom: 0 !important; + } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pb-sm-3 { + padding-bottom: 1rem !important; + } + + .pb-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pb-sm-5 { + padding-bottom: 3rem !important; + } + + .ps-sm-0 { + padding-left: 0 !important; + } + + .ps-sm-1 { + padding-left: 0.25rem !important; + } + + .ps-sm-2 { + padding-left: 0.5rem !important; + } + + .ps-sm-3 { + padding-left: 1rem !important; + } + + .ps-sm-4 { + padding-left: 1.5rem !important; + } + + .ps-sm-5 { + padding-left: 3rem !important; + } + + .gap-sm-0 { + gap: 0 !important; + } + + .gap-sm-1 { + gap: 0.25rem !important; + } + + .gap-sm-2 { + gap: 0.5rem !important; + } + + .gap-sm-3 { + gap: 1rem !important; + } + + .gap-sm-4 { + gap: 1.5rem !important; + } + + .gap-sm-5 { + gap: 3rem !important; + } + + .row-gap-sm-0 { + row-gap: 0 !important; + } + + .row-gap-sm-1 { + row-gap: 0.25rem !important; + } + + .row-gap-sm-2 { + row-gap: 0.5rem !important; + } + + .row-gap-sm-3 { + row-gap: 1rem !important; + } + + .row-gap-sm-4 { + row-gap: 1.5rem !important; + } + + .row-gap-sm-5 { + row-gap: 3rem !important; + } + + .column-gap-sm-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-sm-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-sm-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-sm-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-sm-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-sm-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-sm-start { + text-align: left !important; + } + + .text-sm-end { + text-align: right !important; + } + + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .float-md-start { + float: left !important; + } + + .float-md-end { + float: right !important; + } + + .float-md-none { + float: none !important; + } + + .object-fit-md-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-md-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-md-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-md-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-md-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-grid { + display: grid !important; + } + + .d-md-inline-grid { + display: inline-grid !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: flex !important; + } + + .d-md-inline-flex { + display: inline-flex !important; + } + + .d-md-none { + display: none !important; + } + + .flex-md-fill { + flex: 1 1 auto !important; + } + + .flex-md-row { + flex-direction: row !important; + } + + .flex-md-column { + flex-direction: column !important; + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-md-grow-0 { + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-md-wrap { + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-md-start { + justify-content: flex-start !important; + } + + .justify-content-md-end { + justify-content: flex-end !important; + } + + .justify-content-md-center { + justify-content: center !important; + } + + .justify-content-md-between { + justify-content: space-between !important; + } + + .justify-content-md-around { + justify-content: space-around !important; + } + + .justify-content-md-evenly { + justify-content: space-evenly !important; + } + + .align-items-md-start { + align-items: flex-start !important; + } + + .align-items-md-end { + align-items: flex-end !important; + } + + .align-items-md-center { + align-items: center !important; + } + + .align-items-md-baseline { + align-items: baseline !important; + } + + .align-items-md-stretch { + align-items: stretch !important; + } + + .align-content-md-start { + align-content: flex-start !important; + } + + .align-content-md-end { + align-content: flex-end !important; + } + + .align-content-md-center { + align-content: center !important; + } + + .align-content-md-between { + align-content: space-between !important; + } + + .align-content-md-around { + align-content: space-around !important; + } + + .align-content-md-stretch { + align-content: stretch !important; + } + + .align-self-md-auto { + align-self: auto !important; + } + + .align-self-md-start { + align-self: flex-start !important; + } + + .align-self-md-end { + align-self: flex-end !important; + } + + .align-self-md-center { + align-self: center !important; + } + + .align-self-md-baseline { + align-self: baseline !important; + } + + .align-self-md-stretch { + align-self: stretch !important; + } + + .order-md-first { + order: -1 !important; + } + + .order-md-0 { + order: 0 !important; + } + + .order-md-1 { + order: 1 !important; + } + + .order-md-2 { + order: 2 !important; + } + + .order-md-3 { + order: 3 !important; + } + + .order-md-4 { + order: 4 !important; + } + + .order-md-5 { + order: 5 !important; + } + + .order-md-last { + order: 6 !important; + } + + .m-md-0 { + margin: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mx-md-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-md-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-md-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-md-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-md-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-md-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-md-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-md-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-md-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-md-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-md-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-md-0 { + margin-top: 0 !important; + } + + .mt-md-1 { + margin-top: 0.25rem !important; + } + + .mt-md-2 { + margin-top: 0.5rem !important; + } + + .mt-md-3 { + margin-top: 1rem !important; + } + + .mt-md-4 { + margin-top: 1.5rem !important; + } + + .mt-md-5 { + margin-top: 3rem !important; + } + + .mt-md-auto { + margin-top: auto !important; + } + + .me-md-0 { + margin-right: 0 !important; + } + + .me-md-1 { + margin-right: 0.25rem !important; + } + + .me-md-2 { + margin-right: 0.5rem !important; + } + + .me-md-3 { + margin-right: 1rem !important; + } + + .me-md-4 { + margin-right: 1.5rem !important; + } + + .me-md-5 { + margin-right: 3rem !important; + } + + .me-md-auto { + margin-right: auto !important; + } + + .mb-md-0 { + margin-bottom: 0 !important; + } + + .mb-md-1 { + margin-bottom: 0.25rem !important; + } + + .mb-md-2 { + margin-bottom: 0.5rem !important; + } + + .mb-md-3 { + margin-bottom: 1rem !important; + } + + .mb-md-4 { + margin-bottom: 1.5rem !important; + } + + .mb-md-5 { + margin-bottom: 3rem !important; + } + + .mb-md-auto { + margin-bottom: auto !important; + } + + .ms-md-0 { + margin-left: 0 !important; + } + + .ms-md-1 { + margin-left: 0.25rem !important; + } + + .ms-md-2 { + margin-left: 0.5rem !important; + } + + .ms-md-3 { + margin-left: 1rem !important; + } + + .ms-md-4 { + margin-left: 1.5rem !important; + } + + .ms-md-5 { + margin-left: 3rem !important; + } + + .ms-md-auto { + margin-left: auto !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .px-md-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-md-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-md-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-md-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-md-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-md-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-md-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-md-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-md-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-md-0 { + padding-top: 0 !important; + } + + .pt-md-1 { + padding-top: 0.25rem !important; + } + + .pt-md-2 { + padding-top: 0.5rem !important; + } + + .pt-md-3 { + padding-top: 1rem !important; + } + + .pt-md-4 { + padding-top: 1.5rem !important; + } + + .pt-md-5 { + padding-top: 3rem !important; + } + + .pe-md-0 { + padding-right: 0 !important; + } + + .pe-md-1 { + padding-right: 0.25rem !important; + } + + .pe-md-2 { + padding-right: 0.5rem !important; + } + + .pe-md-3 { + padding-right: 1rem !important; + } + + .pe-md-4 { + padding-right: 1.5rem !important; + } + + .pe-md-5 { + padding-right: 3rem !important; + } + + .pb-md-0 { + padding-bottom: 0 !important; + } + + .pb-md-1 { + padding-bottom: 0.25rem !important; + } + + .pb-md-2 { + padding-bottom: 0.5rem !important; + } + + .pb-md-3 { + padding-bottom: 1rem !important; + } + + .pb-md-4 { + padding-bottom: 1.5rem !important; + } + + .pb-md-5 { + padding-bottom: 3rem !important; + } + + .ps-md-0 { + padding-left: 0 !important; + } + + .ps-md-1 { + padding-left: 0.25rem !important; + } + + .ps-md-2 { + padding-left: 0.5rem !important; + } + + .ps-md-3 { + padding-left: 1rem !important; + } + + .ps-md-4 { + padding-left: 1.5rem !important; + } + + .ps-md-5 { + padding-left: 3rem !important; + } + + .gap-md-0 { + gap: 0 !important; + } + + .gap-md-1 { + gap: 0.25rem !important; + } + + .gap-md-2 { + gap: 0.5rem !important; + } + + .gap-md-3 { + gap: 1rem !important; + } + + .gap-md-4 { + gap: 1.5rem !important; + } + + .gap-md-5 { + gap: 3rem !important; + } + + .row-gap-md-0 { + row-gap: 0 !important; + } + + .row-gap-md-1 { + row-gap: 0.25rem !important; + } + + .row-gap-md-2 { + row-gap: 0.5rem !important; + } + + .row-gap-md-3 { + row-gap: 1rem !important; + } + + .row-gap-md-4 { + row-gap: 1.5rem !important; + } + + .row-gap-md-5 { + row-gap: 3rem !important; + } + + .column-gap-md-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-md-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-md-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-md-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-md-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-md-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-md-start { + text-align: left !important; + } + + .text-md-end { + text-align: right !important; + } + + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .float-lg-start { + float: left !important; + } + + .float-lg-end { + float: right !important; + } + + .float-lg-none { + float: none !important; + } + + .object-fit-lg-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-lg-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-lg-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-lg-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-lg-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-grid { + display: grid !important; + } + + .d-lg-inline-grid { + display: inline-grid !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: flex !important; + } + + .d-lg-inline-flex { + display: inline-flex !important; + } + + .d-lg-none { + display: none !important; + } + + .flex-lg-fill { + flex: 1 1 auto !important; + } + + .flex-lg-row { + flex-direction: row !important; + } + + .flex-lg-column { + flex-direction: column !important; + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-lg-wrap { + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-lg-start { + justify-content: flex-start !important; + } + + .justify-content-lg-end { + justify-content: flex-end !important; + } + + .justify-content-lg-center { + justify-content: center !important; + } + + .justify-content-lg-between { + justify-content: space-between !important; + } + + .justify-content-lg-around { + justify-content: space-around !important; + } + + .justify-content-lg-evenly { + justify-content: space-evenly !important; + } + + .align-items-lg-start { + align-items: flex-start !important; + } + + .align-items-lg-end { + align-items: flex-end !important; + } + + .align-items-lg-center { + align-items: center !important; + } + + .align-items-lg-baseline { + align-items: baseline !important; + } + + .align-items-lg-stretch { + align-items: stretch !important; + } + + .align-content-lg-start { + align-content: flex-start !important; + } + + .align-content-lg-end { + align-content: flex-end !important; + } + + .align-content-lg-center { + align-content: center !important; + } + + .align-content-lg-between { + align-content: space-between !important; + } + + .align-content-lg-around { + align-content: space-around !important; + } + + .align-content-lg-stretch { + align-content: stretch !important; + } + + .align-self-lg-auto { + align-self: auto !important; + } + + .align-self-lg-start { + align-self: flex-start !important; + } + + .align-self-lg-end { + align-self: flex-end !important; + } + + .align-self-lg-center { + align-self: center !important; + } + + .align-self-lg-baseline { + align-self: baseline !important; + } + + .align-self-lg-stretch { + align-self: stretch !important; + } + + .order-lg-first { + order: -1 !important; + } + + .order-lg-0 { + order: 0 !important; + } + + .order-lg-1 { + order: 1 !important; + } + + .order-lg-2 { + order: 2 !important; + } + + .order-lg-3 { + order: 3 !important; + } + + .order-lg-4 { + order: 4 !important; + } + + .order-lg-5 { + order: 5 !important; + } + + .order-lg-last { + order: 6 !important; + } + + .m-lg-0 { + margin: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mx-lg-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-lg-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-lg-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-lg-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-lg-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-lg-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-lg-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-lg-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-lg-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-lg-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-lg-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-lg-0 { + margin-top: 0 !important; + } + + .mt-lg-1 { + margin-top: 0.25rem !important; + } + + .mt-lg-2 { + margin-top: 0.5rem !important; + } + + .mt-lg-3 { + margin-top: 1rem !important; + } + + .mt-lg-4 { + margin-top: 1.5rem !important; + } + + .mt-lg-5 { + margin-top: 3rem !important; + } + + .mt-lg-auto { + margin-top: auto !important; + } + + .me-lg-0 { + margin-right: 0 !important; + } + + .me-lg-1 { + margin-right: 0.25rem !important; + } + + .me-lg-2 { + margin-right: 0.5rem !important; + } + + .me-lg-3 { + margin-right: 1rem !important; + } + + .me-lg-4 { + margin-right: 1.5rem !important; + } + + .me-lg-5 { + margin-right: 3rem !important; + } + + .me-lg-auto { + margin-right: auto !important; + } + + .mb-lg-0 { + margin-bottom: 0 !important; + } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; + } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; + } + + .mb-lg-3 { + margin-bottom: 1rem !important; + } + + .mb-lg-4 { + margin-bottom: 1.5rem !important; + } + + .mb-lg-5 { + margin-bottom: 3rem !important; + } + + .mb-lg-auto { + margin-bottom: auto !important; + } + + .ms-lg-0 { + margin-left: 0 !important; + } + + .ms-lg-1 { + margin-left: 0.25rem !important; + } + + .ms-lg-2 { + margin-left: 0.5rem !important; + } + + .ms-lg-3 { + margin-left: 1rem !important; + } + + .ms-lg-4 { + margin-left: 1.5rem !important; + } + + .ms-lg-5 { + margin-left: 3rem !important; + } + + .ms-lg-auto { + margin-left: auto !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .px-lg-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-lg-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-lg-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-lg-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-lg-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-lg-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-lg-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-lg-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-lg-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-lg-0 { + padding-top: 0 !important; + } + + .pt-lg-1 { + padding-top: 0.25rem !important; + } + + .pt-lg-2 { + padding-top: 0.5rem !important; + } + + .pt-lg-3 { + padding-top: 1rem !important; + } + + .pt-lg-4 { + padding-top: 1.5rem !important; + } + + .pt-lg-5 { + padding-top: 3rem !important; + } + + .pe-lg-0 { + padding-right: 0 !important; + } + + .pe-lg-1 { + padding-right: 0.25rem !important; + } + + .pe-lg-2 { + padding-right: 0.5rem !important; + } + + .pe-lg-3 { + padding-right: 1rem !important; + } + + .pe-lg-4 { + padding-right: 1.5rem !important; + } + + .pe-lg-5 { + padding-right: 3rem !important; + } + + .pb-lg-0 { + padding-bottom: 0 !important; + } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pb-lg-3 { + padding-bottom: 1rem !important; + } + + .pb-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pb-lg-5 { + padding-bottom: 3rem !important; + } + + .ps-lg-0 { + padding-left: 0 !important; + } + + .ps-lg-1 { + padding-left: 0.25rem !important; + } + + .ps-lg-2 { + padding-left: 0.5rem !important; + } + + .ps-lg-3 { + padding-left: 1rem !important; + } + + .ps-lg-4 { + padding-left: 1.5rem !important; + } + + .ps-lg-5 { + padding-left: 3rem !important; + } + + .gap-lg-0 { + gap: 0 !important; + } + + .gap-lg-1 { + gap: 0.25rem !important; + } + + .gap-lg-2 { + gap: 0.5rem !important; + } + + .gap-lg-3 { + gap: 1rem !important; + } + + .gap-lg-4 { + gap: 1.5rem !important; + } + + .gap-lg-5 { + gap: 3rem !important; + } + + .row-gap-lg-0 { + row-gap: 0 !important; + } + + .row-gap-lg-1 { + row-gap: 0.25rem !important; + } + + .row-gap-lg-2 { + row-gap: 0.5rem !important; + } + + .row-gap-lg-3 { + row-gap: 1rem !important; + } + + .row-gap-lg-4 { + row-gap: 1.5rem !important; + } + + .row-gap-lg-5 { + row-gap: 3rem !important; + } + + .column-gap-lg-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-lg-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-lg-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-lg-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-lg-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-lg-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-lg-start { + text-align: left !important; + } + + .text-lg-end { + text-align: right !important; + } + + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .float-xl-start { + float: left !important; + } + + .float-xl-end { + float: right !important; + } + + .float-xl-none { + float: none !important; + } + + .object-fit-xl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-grid { + display: grid !important; + } + + .d-xl-inline-grid { + display: inline-grid !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: flex !important; + } + + .d-xl-inline-flex { + display: inline-flex !important; + } + + .d-xl-none { + display: none !important; + } + + .flex-xl-fill { + flex: 1 1 auto !important; + } + + .flex-xl-row { + flex-direction: row !important; + } + + .flex-xl-column { + flex-direction: column !important; + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xl-wrap { + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xl-start { + justify-content: flex-start !important; + } + + .justify-content-xl-end { + justify-content: flex-end !important; + } + + .justify-content-xl-center { + justify-content: center !important; + } + + .justify-content-xl-between { + justify-content: space-between !important; + } + + .justify-content-xl-around { + justify-content: space-around !important; + } + + .justify-content-xl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xl-start { + align-items: flex-start !important; + } + + .align-items-xl-end { + align-items: flex-end !important; + } + + .align-items-xl-center { + align-items: center !important; + } + + .align-items-xl-baseline { + align-items: baseline !important; + } + + .align-items-xl-stretch { + align-items: stretch !important; + } + + .align-content-xl-start { + align-content: flex-start !important; + } + + .align-content-xl-end { + align-content: flex-end !important; + } + + .align-content-xl-center { + align-content: center !important; + } + + .align-content-xl-between { + align-content: space-between !important; + } + + .align-content-xl-around { + align-content: space-around !important; + } + + .align-content-xl-stretch { + align-content: stretch !important; + } + + .align-self-xl-auto { + align-self: auto !important; + } + + .align-self-xl-start { + align-self: flex-start !important; + } + + .align-self-xl-end { + align-self: flex-end !important; + } + + .align-self-xl-center { + align-self: center !important; + } + + .align-self-xl-baseline { + align-self: baseline !important; + } + + .align-self-xl-stretch { + align-self: stretch !important; + } + + .order-xl-first { + order: -1 !important; + } + + .order-xl-0 { + order: 0 !important; + } + + .order-xl-1 { + order: 1 !important; + } + + .order-xl-2 { + order: 2 !important; + } + + .order-xl-3 { + order: 3 !important; + } + + .order-xl-4 { + order: 4 !important; + } + + .order-xl-5 { + order: 5 !important; + } + + .order-xl-last { + order: 6 !important; + } + + .m-xl-0 { + margin: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mx-xl-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-xl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-xl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-xl-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-xl-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-xl-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-xl-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xl-0 { + margin-top: 0 !important; + } + + .mt-xl-1 { + margin-top: 0.25rem !important; + } + + .mt-xl-2 { + margin-top: 0.5rem !important; + } + + .mt-xl-3 { + margin-top: 1rem !important; + } + + .mt-xl-4 { + margin-top: 1.5rem !important; + } + + .mt-xl-5 { + margin-top: 3rem !important; + } + + .mt-xl-auto { + margin-top: auto !important; + } + + .me-xl-0 { + margin-right: 0 !important; + } + + .me-xl-1 { + margin-right: 0.25rem !important; + } + + .me-xl-2 { + margin-right: 0.5rem !important; + } + + .me-xl-3 { + margin-right: 1rem !important; + } + + .me-xl-4 { + margin-right: 1.5rem !important; + } + + .me-xl-5 { + margin-right: 3rem !important; + } + + .me-xl-auto { + margin-right: auto !important; + } + + .mb-xl-0 { + margin-bottom: 0 !important; + } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xl-3 { + margin-bottom: 1rem !important; + } + + .mb-xl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xl-5 { + margin-bottom: 3rem !important; + } + + .mb-xl-auto { + margin-bottom: auto !important; + } + + .ms-xl-0 { + margin-left: 0 !important; + } + + .ms-xl-1 { + margin-left: 0.25rem !important; + } + + .ms-xl-2 { + margin-left: 0.5rem !important; + } + + .ms-xl-3 { + margin-left: 1rem !important; + } + + .ms-xl-4 { + margin-left: 1.5rem !important; + } + + .ms-xl-5 { + margin-left: 3rem !important; + } + + .ms-xl-auto { + margin-left: auto !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .px-xl-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-xl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-xl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-xl-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-xl-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-xl-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xl-0 { + padding-top: 0 !important; + } + + .pt-xl-1 { + padding-top: 0.25rem !important; + } + + .pt-xl-2 { + padding-top: 0.5rem !important; + } + + .pt-xl-3 { + padding-top: 1rem !important; + } + + .pt-xl-4 { + padding-top: 1.5rem !important; + } + + .pt-xl-5 { + padding-top: 3rem !important; + } + + .pe-xl-0 { + padding-right: 0 !important; + } + + .pe-xl-1 { + padding-right: 0.25rem !important; + } + + .pe-xl-2 { + padding-right: 0.5rem !important; + } + + .pe-xl-3 { + padding-right: 1rem !important; + } + + .pe-xl-4 { + padding-right: 1.5rem !important; + } + + .pe-xl-5 { + padding-right: 3rem !important; + } + + .pb-xl-0 { + padding-bottom: 0 !important; + } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xl-3 { + padding-bottom: 1rem !important; + } + + .pb-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xl-5 { + padding-bottom: 3rem !important; + } + + .ps-xl-0 { + padding-left: 0 !important; + } + + .ps-xl-1 { + padding-left: 0.25rem !important; + } + + .ps-xl-2 { + padding-left: 0.5rem !important; + } + + .ps-xl-3 { + padding-left: 1rem !important; + } + + .ps-xl-4 { + padding-left: 1.5rem !important; + } + + .ps-xl-5 { + padding-left: 3rem !important; + } + + .gap-xl-0 { + gap: 0 !important; + } + + .gap-xl-1 { + gap: 0.25rem !important; + } + + .gap-xl-2 { + gap: 0.5rem !important; + } + + .gap-xl-3 { + gap: 1rem !important; + } + + .gap-xl-4 { + gap: 1.5rem !important; + } + + .gap-xl-5 { + gap: 3rem !important; + } + + .row-gap-xl-0 { + row-gap: 0 !important; + } + + .row-gap-xl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xl-3 { + row-gap: 1rem !important; + } + + .row-gap-xl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xl-5 { + row-gap: 3rem !important; + } + + .column-gap-xl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xl-start { + text-align: left !important; + } + + .text-xl-end { + text-align: right !important; + } + + .text-xl-center { + text-align: center !important; + } +} + +@media (min-width: 1400px) { + .float-xxl-start { + float: left !important; + } + + .float-xxl-end { + float: right !important; + } + + .float-xxl-none { + float: none !important; + } + + .object-fit-xxl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xxl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xxl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xxl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xxl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xxl-inline { + display: inline !important; + } + + .d-xxl-inline-block { + display: inline-block !important; + } + + .d-xxl-block { + display: block !important; + } + + .d-xxl-grid { + display: grid !important; + } + + .d-xxl-inline-grid { + display: inline-grid !important; + } + + .d-xxl-table { + display: table !important; + } + + .d-xxl-table-row { + display: table-row !important; + } + + .d-xxl-table-cell { + display: table-cell !important; + } + + .d-xxl-flex { + display: flex !important; + } + + .d-xxl-inline-flex { + display: inline-flex !important; + } + + .d-xxl-none { + display: none !important; + } + + .flex-xxl-fill { + flex: 1 1 auto !important; + } + + .flex-xxl-row { + flex-direction: row !important; + } + + .flex-xxl-column { + flex-direction: column !important; + } + + .flex-xxl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xxl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xxl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xxl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xxl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xxl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xxl-wrap { + flex-wrap: wrap !important; + } + + .flex-xxl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xxl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xxl-start { + justify-content: flex-start !important; + } + + .justify-content-xxl-end { + justify-content: flex-end !important; + } + + .justify-content-xxl-center { + justify-content: center !important; + } + + .justify-content-xxl-between { + justify-content: space-between !important; + } + + .justify-content-xxl-around { + justify-content: space-around !important; + } + + .justify-content-xxl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xxl-start { + align-items: flex-start !important; + } + + .align-items-xxl-end { + align-items: flex-end !important; + } + + .align-items-xxl-center { + align-items: center !important; + } + + .align-items-xxl-baseline { + align-items: baseline !important; + } + + .align-items-xxl-stretch { + align-items: stretch !important; + } + + .align-content-xxl-start { + align-content: flex-start !important; + } + + .align-content-xxl-end { + align-content: flex-end !important; + } + + .align-content-xxl-center { + align-content: center !important; + } + + .align-content-xxl-between { + align-content: space-between !important; + } + + .align-content-xxl-around { + align-content: space-around !important; + } + + .align-content-xxl-stretch { + align-content: stretch !important; + } + + .align-self-xxl-auto { + align-self: auto !important; + } + + .align-self-xxl-start { + align-self: flex-start !important; + } + + .align-self-xxl-end { + align-self: flex-end !important; + } + + .align-self-xxl-center { + align-self: center !important; + } + + .align-self-xxl-baseline { + align-self: baseline !important; + } + + .align-self-xxl-stretch { + align-self: stretch !important; + } + + .order-xxl-first { + order: -1 !important; + } + + .order-xxl-0 { + order: 0 !important; + } + + .order-xxl-1 { + order: 1 !important; + } + + .order-xxl-2 { + order: 2 !important; + } + + .order-xxl-3 { + order: 3 !important; + } + + .order-xxl-4 { + order: 4 !important; + } + + .order-xxl-5 { + order: 5 !important; + } + + .order-xxl-last { + order: 6 !important; + } + + .m-xxl-0 { + margin: 0 !important; + } + + .m-xxl-1 { + margin: 0.25rem !important; + } + + .m-xxl-2 { + margin: 0.5rem !important; + } + + .m-xxl-3 { + margin: 1rem !important; + } + + .m-xxl-4 { + margin: 1.5rem !important; + } + + .m-xxl-5 { + margin: 3rem !important; + } + + .m-xxl-auto { + margin: auto !important; + } + + .mx-xxl-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-xxl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-xxl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-xxl-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-xxl-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-xxl-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-xxl-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-xxl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xxl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xxl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xxl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xxl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xxl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xxl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xxl-0 { + margin-top: 0 !important; + } + + .mt-xxl-1 { + margin-top: 0.25rem !important; + } + + .mt-xxl-2 { + margin-top: 0.5rem !important; + } + + .mt-xxl-3 { + margin-top: 1rem !important; + } + + .mt-xxl-4 { + margin-top: 1.5rem !important; + } + + .mt-xxl-5 { + margin-top: 3rem !important; + } + + .mt-xxl-auto { + margin-top: auto !important; + } + + .me-xxl-0 { + margin-right: 0 !important; + } + + .me-xxl-1 { + margin-right: 0.25rem !important; + } + + .me-xxl-2 { + margin-right: 0.5rem !important; + } + + .me-xxl-3 { + margin-right: 1rem !important; + } + + .me-xxl-4 { + margin-right: 1.5rem !important; + } + + .me-xxl-5 { + margin-right: 3rem !important; + } + + .me-xxl-auto { + margin-right: auto !important; + } + + .mb-xxl-0 { + margin-bottom: 0 !important; + } + + .mb-xxl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xxl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xxl-3 { + margin-bottom: 1rem !important; + } + + .mb-xxl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xxl-5 { + margin-bottom: 3rem !important; + } + + .mb-xxl-auto { + margin-bottom: auto !important; + } + + .ms-xxl-0 { + margin-left: 0 !important; + } + + .ms-xxl-1 { + margin-left: 0.25rem !important; + } + + .ms-xxl-2 { + margin-left: 0.5rem !important; + } + + .ms-xxl-3 { + margin-left: 1rem !important; + } + + .ms-xxl-4 { + margin-left: 1.5rem !important; + } + + .ms-xxl-5 { + margin-left: 3rem !important; + } + + .ms-xxl-auto { + margin-left: auto !important; + } + + .p-xxl-0 { + padding: 0 !important; + } + + .p-xxl-1 { + padding: 0.25rem !important; + } + + .p-xxl-2 { + padding: 0.5rem !important; + } + + .p-xxl-3 { + padding: 1rem !important; + } + + .p-xxl-4 { + padding: 1.5rem !important; + } + + .p-xxl-5 { + padding: 3rem !important; + } + + .px-xxl-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-xxl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-xxl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-xxl-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-xxl-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-xxl-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-xxl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xxl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xxl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xxl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xxl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xxl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xxl-0 { + padding-top: 0 !important; + } + + .pt-xxl-1 { + padding-top: 0.25rem !important; + } + + .pt-xxl-2 { + padding-top: 0.5rem !important; + } + + .pt-xxl-3 { + padding-top: 1rem !important; + } + + .pt-xxl-4 { + padding-top: 1.5rem !important; + } + + .pt-xxl-5 { + padding-top: 3rem !important; + } + + .pe-xxl-0 { + padding-right: 0 !important; + } + + .pe-xxl-1 { + padding-right: 0.25rem !important; + } + + .pe-xxl-2 { + padding-right: 0.5rem !important; + } + + .pe-xxl-3 { + padding-right: 1rem !important; + } + + .pe-xxl-4 { + padding-right: 1.5rem !important; + } + + .pe-xxl-5 { + padding-right: 3rem !important; + } + + .pb-xxl-0 { + padding-bottom: 0 !important; + } + + .pb-xxl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xxl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xxl-3 { + padding-bottom: 1rem !important; + } + + .pb-xxl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xxl-5 { + padding-bottom: 3rem !important; + } + + .ps-xxl-0 { + padding-left: 0 !important; + } + + .ps-xxl-1 { + padding-left: 0.25rem !important; + } + + .ps-xxl-2 { + padding-left: 0.5rem !important; + } + + .ps-xxl-3 { + padding-left: 1rem !important; + } + + .ps-xxl-4 { + padding-left: 1.5rem !important; + } + + .ps-xxl-5 { + padding-left: 3rem !important; + } + + .gap-xxl-0 { + gap: 0 !important; + } + + .gap-xxl-1 { + gap: 0.25rem !important; + } + + .gap-xxl-2 { + gap: 0.5rem !important; + } + + .gap-xxl-3 { + gap: 1rem !important; + } + + .gap-xxl-4 { + gap: 1.5rem !important; + } + + .gap-xxl-5 { + gap: 3rem !important; + } + + .row-gap-xxl-0 { + row-gap: 0 !important; + } + + .row-gap-xxl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xxl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xxl-3 { + row-gap: 1rem !important; + } + + .row-gap-xxl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xxl-5 { + row-gap: 3rem !important; + } + + .column-gap-xxl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xxl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xxl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xxl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xxl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xxl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xxl-start { + text-align: left !important; + } + + .text-xxl-end { + text-align: right !important; + } + + .text-xxl-center { + text-align: center !important; + } } + @media (min-width: 1200px) { - .fs-1 { - font-size: 2.5rem !important; - } - .fs-2 { - font-size: 2rem !important; - } - .fs-3 { - font-size: 1.75rem !important; - } - .fs-4 { - font-size: 1.5rem !important; - } + .fs-1 { + font-size: 2.5rem !important; + } + + .fs-2 { + font-size: 2rem !important; + } + + .fs-3 { + font-size: 1.75rem !important; + } + + .fs-4 { + font-size: 1.5rem !important; + } } + @media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-grid { + display: grid !important; + } + + .d-print-inline-grid { + display: inline-grid !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: flex !important; + } + + .d-print-inline-flex { + display: inline-flex !important; + } + + .d-print-none { + display: none !important; + } } -/*# sourceMappingURL=bootstrap-utilities.css.map */ \ No newline at end of file +/*# sourceMappingURL=bootstrap-utilities.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css index 2d71422372..85cee7ea9c 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css @@ -5,5389 +5,6299 @@ */ :root, [data-bs-theme=light] { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-black: #000; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #0d6efd; - --bs-secondary: #6c757d; - --bs-success: #198754; - --bs-info: #0dcaf0; - --bs-warning: #ffc107; - --bs-danger: #dc3545; - --bs-light: #f8f9fa; - --bs-dark: #212529; - --bs-primary-rgb: 13, 110, 253; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 25, 135, 84; - --bs-info-rgb: 13, 202, 240; - --bs-warning-rgb: 255, 193, 7; - --bs-danger-rgb: 220, 53, 69; - --bs-light-rgb: 248, 249, 250; - --bs-dark-rgb: 33, 37, 41; - --bs-primary-text-emphasis: #052c65; - --bs-secondary-text-emphasis: #2b2f32; - --bs-success-text-emphasis: #0a3622; - --bs-info-text-emphasis: #055160; - --bs-warning-text-emphasis: #664d03; - --bs-danger-text-emphasis: #58151c; - --bs-light-text-emphasis: #495057; - --bs-dark-text-emphasis: #495057; - --bs-primary-bg-subtle: #cfe2ff; - --bs-secondary-bg-subtle: #e2e3e5; - --bs-success-bg-subtle: #d1e7dd; - --bs-info-bg-subtle: #cff4fc; - --bs-warning-bg-subtle: #fff3cd; - --bs-danger-bg-subtle: #f8d7da; - --bs-light-bg-subtle: #fcfcfd; - --bs-dark-bg-subtle: #ced4da; - --bs-primary-border-subtle: #9ec5fe; - --bs-secondary-border-subtle: #c4c8cb; - --bs-success-border-subtle: #a3cfbb; - --bs-info-border-subtle: #9eeaf9; - --bs-warning-border-subtle: #ffe69c; - --bs-danger-border-subtle: #f1aeb5; - --bs-light-border-subtle: #e9ecef; - --bs-dark-border-subtle: #adb5bd; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-body-font-family: var(--bs-font-sans-serif); - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #212529; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg: #fff; - --bs-body-bg-rgb: 255, 255, 255; - --bs-emphasis-color: #000; - --bs-emphasis-color-rgb: 0, 0, 0; - --bs-secondary-color: rgba(33, 37, 41, 0.75); - --bs-secondary-color-rgb: 33, 37, 41; - --bs-secondary-bg: #e9ecef; - --bs-secondary-bg-rgb: 233, 236, 239; - --bs-tertiary-color: rgba(33, 37, 41, 0.5); - --bs-tertiary-color-rgb: 33, 37, 41; - --bs-tertiary-bg: #f8f9fa; - --bs-tertiary-bg-rgb: 248, 249, 250; - --bs-heading-color: inherit; - --bs-link-color: #0d6efd; - --bs-link-color-rgb: 13, 110, 253; - --bs-link-decoration: underline; - --bs-link-hover-color: #0a58ca; - --bs-link-hover-color-rgb: 10, 88, 202; - --bs-code-color: #d63384; - --bs-highlight-color: #212529; - --bs-highlight-bg: #fff3cd; - --bs-border-width: 1px; - --bs-border-style: solid; - --bs-border-color: #dee2e6; - --bs-border-color-translucent: rgba(0, 0, 0, 0.175); - --bs-border-radius: 0.375rem; - --bs-border-radius-sm: 0.25rem; - --bs-border-radius-lg: 0.5rem; - --bs-border-radius-xl: 1rem; - --bs-border-radius-xxl: 2rem; - --bs-border-radius-2xl: var(--bs-border-radius-xxl); - --bs-border-radius-pill: 50rem; - --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); - --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); - --bs-focus-ring-width: 0.25rem; - --bs-focus-ring-opacity: 0.25; - --bs-focus-ring-color: rgba(13, 110, 253, 0.25); - --bs-form-valid-color: #198754; - --bs-form-valid-border-color: #198754; - --bs-form-invalid-color: #dc3545; - --bs-form-invalid-border-color: #dc3545; + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-black: #000; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #0d6efd; + --bs-secondary: #6c757d; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-primary-rgb: 13, 110, 253; + --bs-secondary-rgb: 108, 117, 125; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-primary-text-emphasis: #052c65; + --bs-secondary-text-emphasis: #2b2f32; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #cfe2ff; + --bs-secondary-bg-subtle: #e2e3e5; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-primary-border-subtle: #9ec5fe; + --bs-secondary-border-subtle: #c4c8cb; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-body-font-family: var(--bs-font-sans-serif); + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #0d6efd; + --bs-link-color-rgb: 13, 110, 253; + --bs-link-decoration: underline; + --bs-link-hover-color: #0a58ca; + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-code-color: #d63384; + --bs-highlight-color: #212529; + --bs-highlight-bg: #fff3cd; + --bs-border-width: 1px; + --bs-border-style: solid; + --bs-border-color: #dee2e6; + --bs-border-color-translucent: rgba(0, 0, 0, 0.175); + --bs-border-radius: 0.375rem; + --bs-border-radius-sm: 0.25rem; + --bs-border-radius-lg: 0.5rem; + --bs-border-radius-xl: 1rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); + --bs-border-radius-pill: 50rem; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(13, 110, 253, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; } [data-bs-theme=dark] { - color-scheme: dark; - --bs-body-color: #dee2e6; - --bs-body-color-rgb: 222, 226, 230; - --bs-body-bg: #212529; - --bs-body-bg-rgb: 33, 37, 41; - --bs-emphasis-color: #fff; - --bs-emphasis-color-rgb: 255, 255, 255; - --bs-secondary-color: rgba(222, 226, 230, 0.75); - --bs-secondary-color-rgb: 222, 226, 230; - --bs-secondary-bg: #343a40; - --bs-secondary-bg-rgb: 52, 58, 64; - --bs-tertiary-color: rgba(222, 226, 230, 0.5); - --bs-tertiary-color-rgb: 222, 226, 230; - --bs-tertiary-bg: #2b3035; - --bs-tertiary-bg-rgb: 43, 48, 53; - --bs-primary-text-emphasis: #6ea8fe; - --bs-secondary-text-emphasis: #a7acb1; - --bs-success-text-emphasis: #75b798; - --bs-info-text-emphasis: #6edff6; - --bs-warning-text-emphasis: #ffda6a; - --bs-danger-text-emphasis: #ea868f; - --bs-light-text-emphasis: #f8f9fa; - --bs-dark-text-emphasis: #dee2e6; - --bs-primary-bg-subtle: #031633; - --bs-secondary-bg-subtle: #161719; - --bs-success-bg-subtle: #051b11; - --bs-info-bg-subtle: #032830; - --bs-warning-bg-subtle: #332701; - --bs-danger-bg-subtle: #2c0b0e; - --bs-light-bg-subtle: #343a40; - --bs-dark-bg-subtle: #1a1d20; - --bs-primary-border-subtle: #084298; - --bs-secondary-border-subtle: #41464b; - --bs-success-border-subtle: #0f5132; - --bs-info-border-subtle: #087990; - --bs-warning-border-subtle: #997404; - --bs-danger-border-subtle: #842029; - --bs-light-border-subtle: #495057; - --bs-dark-border-subtle: #343a40; - --bs-heading-color: inherit; - --bs-link-color: #6ea8fe; - --bs-link-hover-color: #8bb9fe; - --bs-link-color-rgb: 110, 168, 254; - --bs-link-hover-color-rgb: 139, 185, 254; - --bs-code-color: #e685b5; - --bs-highlight-color: #dee2e6; - --bs-highlight-bg: #664d03; - --bs-border-color: #495057; - --bs-border-color-translucent: rgba(255, 255, 255, 0.15); - --bs-form-valid-color: #75b798; - --bs-form-valid-border-color: #75b798; - --bs-form-invalid-color: #ea868f; - --bs-form-invalid-border-color: #ea868f; + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #6ea8fe; + --bs-secondary-text-emphasis: #a7acb1; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-primary-bg-subtle: #031633; + --bs-secondary-bg-subtle: #161719; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-primary-border-subtle: #084298; + --bs-secondary-border-subtle: #41464b; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-heading-color: inherit; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-code-color: #e685b5; + --bs-highlight-color: #dee2e6; + --bs-highlight-bg: #664d03; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; } .clearfix::after { - display: block; - clear: both; - content: ""; + display: block; + clear: both; + content: ""; } .text-bg-primary { - color: #fff !important; - background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-secondary { - color: #fff !important; - background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-success { - color: #fff !important; - background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-info { - color: #000 !important; - background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-warning { - color: #000 !important; - background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-danger { - color: #fff !important; - background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-light { - color: #000 !important; - background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-dark { - color: #fff !important; - background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; } .link-primary { - color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-primary:hover, .link-primary:focus { - color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; } .link-secondary { - color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-secondary:hover, .link-secondary:focus { - color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; } .link-success { - color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-success:hover, .link-success:focus { - color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; } .link-info { - color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-info:hover, .link-info:focus { - color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; } .link-warning { - color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-warning:hover, .link-warning:focus { - color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; } .link-danger { - color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-danger:hover, .link-danger:focus { - color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; } .link-light { - color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-light:hover, .link-light:focus { - color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; } .link-dark { - color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-dark:hover, .link-dark:focus { - color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; } .link-body-emphasis { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-body-emphasis:hover, .link-body-emphasis:focus { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; } .focus-ring:focus { - outline: 0; - box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); + outline: 0; + box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); } .icon-link { - display: inline-flex; - gap: 0.375rem; - align-items: center; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-underline-offset: 0.25em; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; + display: inline-flex; + gap: 0.375rem; + align-items: center; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-underline-offset: 0.25em; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + .icon-link > .bi { - flex-shrink: 0; - width: 1em; - height: 1em; - fill: currentcolor; - transition: 0.2s ease-in-out transform; + flex-shrink: 0; + width: 1em; + height: 1em; + fill: currentcolor; + transition: 0.2s ease-in-out transform; } + @media (prefers-reduced-motion: reduce) { - .icon-link > .bi { - transition: none; - } + .icon-link > .bi { + transition: none; + } } .icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi { - transform: var(--bs-icon-link-transform, translate3d(-0.25em, 0, 0)); + transform: var(--bs-icon-link-transform, translate3d(-0.25em, 0, 0)); } .ratio { - position: relative; - width: 100%; + position: relative; + width: 100%; } + .ratio::before { - display: block; - padding-top: var(--bs-aspect-ratio); - content: ""; + display: block; + padding-top: var(--bs-aspect-ratio); + content: ""; } + .ratio > * { - position: absolute; - top: 0; - right: 0; - width: 100%; - height: 100%; + position: absolute; + top: 0; + right: 0; + width: 100%; + height: 100%; } .ratio-1x1 { - --bs-aspect-ratio: 100%; + --bs-aspect-ratio: 100%; } .ratio-4x3 { - --bs-aspect-ratio: 75%; + --bs-aspect-ratio: 75%; } .ratio-16x9 { - --bs-aspect-ratio: 56.25%; + --bs-aspect-ratio: 56.25%; } .ratio-21x9 { - --bs-aspect-ratio: 42.8571428571%; + --bs-aspect-ratio: 42.8571428571%; } .fixed-top { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1030; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1030; } .fixed-bottom { - position: fixed; - left: 0; - bottom: 0; - right: 0; - z-index: 1030; + position: fixed; + left: 0; + bottom: 0; + right: 0; + z-index: 1030; } .sticky-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; -} - -.sticky-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; -} - -@media (min-width: 576px) { - .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; - } - .sticky-sm-bottom { +} + +.sticky-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; - } } + +@media (min-width: 576px) { + .sticky-sm-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-sm-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } +} + @media (min-width: 768px) { - .sticky-md-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-md-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-md-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-md-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 992px) { - .sticky-lg-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-lg-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-lg-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-lg-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1200px) { - .sticky-xl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1400px) { - .sticky-xxl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xxl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xxl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xxl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + .hstack { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; + display: flex; + flex-direction: row; + align-items: center; + align-self: stretch; } .vstack { - display: flex; - flex: 1 1 auto; - flex-direction: column; - align-self: stretch; + display: flex; + flex: 1 1 auto; + flex-direction: column; + align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; } + .visually-hidden:not(caption), .visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) { - position: absolute !important; + position: absolute !important; } .stretched-link::after { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - z-index: 1; - content: ""; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 1; + content: ""; } .text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .vr { - display: inline-block; - align-self: stretch; - width: var(--bs-border-width); - min-height: 1em; - background-color: currentcolor; - opacity: 0.25; + display: inline-block; + align-self: stretch; + width: var(--bs-border-width); + min-height: 1em; + background-color: currentcolor; + opacity: 0.25; } .align-baseline { - vertical-align: baseline !important; + vertical-align: baseline !important; } .align-top { - vertical-align: top !important; + vertical-align: top !important; } .align-middle { - vertical-align: middle !important; + vertical-align: middle !important; } .align-bottom { - vertical-align: bottom !important; + vertical-align: bottom !important; } .align-text-bottom { - vertical-align: text-bottom !important; + vertical-align: text-bottom !important; } .align-text-top { - vertical-align: text-top !important; + vertical-align: text-top !important; } .float-start { - float: right !important; + float: right !important; } .float-end { - float: left !important; + float: left !important; } .float-none { - float: none !important; + float: none !important; } .object-fit-contain { - -o-object-fit: contain !important; - object-fit: contain !important; + -o-object-fit: contain !important; + object-fit: contain !important; } .object-fit-cover { - -o-object-fit: cover !important; - object-fit: cover !important; + -o-object-fit: cover !important; + object-fit: cover !important; } .object-fit-fill { - -o-object-fit: fill !important; - object-fit: fill !important; + -o-object-fit: fill !important; + object-fit: fill !important; } .object-fit-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; + -o-object-fit: scale-down !important; + object-fit: scale-down !important; } .object-fit-none { - -o-object-fit: none !important; - object-fit: none !important; + -o-object-fit: none !important; + object-fit: none !important; } .opacity-0 { - opacity: 0 !important; + opacity: 0 !important; } .opacity-25 { - opacity: 0.25 !important; + opacity: 0.25 !important; } .opacity-50 { - opacity: 0.5 !important; + opacity: 0.5 !important; } .opacity-75 { - opacity: 0.75 !important; + opacity: 0.75 !important; } .opacity-100 { - opacity: 1 !important; + opacity: 1 !important; } .overflow-auto { - overflow: auto !important; + overflow: auto !important; } .overflow-hidden { - overflow: hidden !important; + overflow: hidden !important; } .overflow-visible { - overflow: visible !important; + overflow: visible !important; } .overflow-scroll { - overflow: scroll !important; + overflow: scroll !important; } .overflow-x-auto { - overflow-x: auto !important; + overflow-x: auto !important; } .overflow-x-hidden { - overflow-x: hidden !important; + overflow-x: hidden !important; } .overflow-x-visible { - overflow-x: visible !important; + overflow-x: visible !important; } .overflow-x-scroll { - overflow-x: scroll !important; + overflow-x: scroll !important; } .overflow-y-auto { - overflow-y: auto !important; + overflow-y: auto !important; } .overflow-y-hidden { - overflow-y: hidden !important; + overflow-y: hidden !important; } .overflow-y-visible { - overflow-y: visible !important; + overflow-y: visible !important; } .overflow-y-scroll { - overflow-y: scroll !important; + overflow-y: scroll !important; } .d-inline { - display: inline !important; + display: inline !important; } .d-inline-block { - display: inline-block !important; + display: inline-block !important; } .d-block { - display: block !important; + display: block !important; } .d-grid { - display: grid !important; + display: grid !important; } .d-inline-grid { - display: inline-grid !important; + display: inline-grid !important; } .d-table { - display: table !important; + display: table !important; } .d-table-row { - display: table-row !important; + display: table-row !important; } .d-table-cell { - display: table-cell !important; + display: table-cell !important; } .d-flex { - display: flex !important; + display: flex !important; } .d-inline-flex { - display: inline-flex !important; + display: inline-flex !important; } .d-none { - display: none !important; + display: none !important; } .shadow { - box-shadow: var(--bs-box-shadow) !important; + box-shadow: var(--bs-box-shadow) !important; } .shadow-sm { - box-shadow: var(--bs-box-shadow-sm) !important; + box-shadow: var(--bs-box-shadow-sm) !important; } .shadow-lg { - box-shadow: var(--bs-box-shadow-lg) !important; + box-shadow: var(--bs-box-shadow-lg) !important; } .shadow-none { - box-shadow: none !important; + box-shadow: none !important; } .focus-ring-primary { - --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-secondary { - --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-success { - --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-info { - --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-warning { - --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-danger { - --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-light { - --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-dark { - --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); } .position-static { - position: static !important; + position: static !important; } .position-relative { - position: relative !important; + position: relative !important; } .position-absolute { - position: absolute !important; + position: absolute !important; } .position-fixed { - position: fixed !important; + position: fixed !important; } .position-sticky { - position: -webkit-sticky !important; - position: sticky !important; + position: -webkit-sticky !important; + position: sticky !important; } .top-0 { - top: 0 !important; + top: 0 !important; } .top-50 { - top: 50% !important; + top: 50% !important; } .top-100 { - top: 100% !important; + top: 100% !important; } .bottom-0 { - bottom: 0 !important; + bottom: 0 !important; } .bottom-50 { - bottom: 50% !important; + bottom: 50% !important; } .bottom-100 { - bottom: 100% !important; + bottom: 100% !important; } .start-0 { - right: 0 !important; + right: 0 !important; } .start-50 { - right: 50% !important; + right: 50% !important; } .start-100 { - right: 100% !important; + right: 100% !important; } .end-0 { - left: 0 !important; + left: 0 !important; } .end-50 { - left: 50% !important; + left: 50% !important; } .end-100 { - left: 100% !important; + left: 100% !important; } .translate-middle { - transform: translate(50%, -50%) !important; + transform: translate(50%, -50%) !important; } .translate-middle-x { - transform: translateX(50%) !important; + transform: translateX(50%) !important; } .translate-middle-y { - transform: translateY(-50%) !important; + transform: translateY(-50%) !important; } .border { - border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-0 { - border: 0 !important; + border: 0 !important; } .border-top { - border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-top-0 { - border-top: 0 !important; + border-top: 0 !important; } .border-end { - border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-end-0 { - border-left: 0 !important; + border-left: 0 !important; } .border-bottom { - border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-bottom-0 { - border-bottom: 0 !important; + border-bottom: 0 !important; } .border-start { - border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-start-0 { - border-right: 0 !important; + border-right: 0 !important; } .border-primary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; } .border-secondary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; } .border-success { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; } .border-info { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; } .border-warning { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; } .border-danger { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; } .border-light { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; } .border-dark { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } .border-black { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; } .border-white { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } .border-primary-subtle { - border-color: var(--bs-primary-border-subtle) !important; + border-color: var(--bs-primary-border-subtle) !important; } .border-secondary-subtle { - border-color: var(--bs-secondary-border-subtle) !important; + border-color: var(--bs-secondary-border-subtle) !important; } .border-success-subtle { - border-color: var(--bs-success-border-subtle) !important; + border-color: var(--bs-success-border-subtle) !important; } .border-info-subtle { - border-color: var(--bs-info-border-subtle) !important; + border-color: var(--bs-info-border-subtle) !important; } .border-warning-subtle { - border-color: var(--bs-warning-border-subtle) !important; + border-color: var(--bs-warning-border-subtle) !important; } .border-danger-subtle { - border-color: var(--bs-danger-border-subtle) !important; + border-color: var(--bs-danger-border-subtle) !important; } .border-light-subtle { - border-color: var(--bs-light-border-subtle) !important; + border-color: var(--bs-light-border-subtle) !important; } .border-dark-subtle { - border-color: var(--bs-dark-border-subtle) !important; + border-color: var(--bs-dark-border-subtle) !important; } .border-1 { - border-width: 1px !important; + border-width: 1px !important; } .border-2 { - border-width: 2px !important; + border-width: 2px !important; } .border-3 { - border-width: 3px !important; + border-width: 3px !important; } .border-4 { - border-width: 4px !important; + border-width: 4px !important; } .border-5 { - border-width: 5px !important; + border-width: 5px !important; } .border-opacity-10 { - --bs-border-opacity: 0.1; + --bs-border-opacity: 0.1; } .border-opacity-25 { - --bs-border-opacity: 0.25; + --bs-border-opacity: 0.25; } .border-opacity-50 { - --bs-border-opacity: 0.5; + --bs-border-opacity: 0.5; } .border-opacity-75 { - --bs-border-opacity: 0.75; + --bs-border-opacity: 0.75; } .border-opacity-100 { - --bs-border-opacity: 1; + --bs-border-opacity: 1; } .w-25 { - width: 25% !important; + width: 25% !important; } .w-50 { - width: 50% !important; + width: 50% !important; } .w-75 { - width: 75% !important; + width: 75% !important; } .w-100 { - width: 100% !important; + width: 100% !important; } .w-auto { - width: auto !important; + width: auto !important; } .mw-100 { - max-width: 100% !important; + max-width: 100% !important; } .vw-100 { - width: 100vw !important; + width: 100vw !important; } .min-vw-100 { - min-width: 100vw !important; + min-width: 100vw !important; } .h-25 { - height: 25% !important; + height: 25% !important; } .h-50 { - height: 50% !important; + height: 50% !important; } .h-75 { - height: 75% !important; + height: 75% !important; } .h-100 { - height: 100% !important; + height: 100% !important; } .h-auto { - height: auto !important; + height: auto !important; } .mh-100 { - max-height: 100% !important; + max-height: 100% !important; } .vh-100 { - height: 100vh !important; + height: 100vh !important; } .min-vh-100 { - min-height: 100vh !important; + min-height: 100vh !important; } .flex-fill { - flex: 1 1 auto !important; + flex: 1 1 auto !important; } .flex-row { - flex-direction: row !important; + flex-direction: row !important; } .flex-column { - flex-direction: column !important; + flex-direction: column !important; } .flex-row-reverse { - flex-direction: row-reverse !important; + flex-direction: row-reverse !important; } .flex-column-reverse { - flex-direction: column-reverse !important; + flex-direction: column-reverse !important; } .flex-grow-0 { - flex-grow: 0 !important; + flex-grow: 0 !important; } .flex-grow-1 { - flex-grow: 1 !important; + flex-grow: 1 !important; } .flex-shrink-0 { - flex-shrink: 0 !important; + flex-shrink: 0 !important; } .flex-shrink-1 { - flex-shrink: 1 !important; + flex-shrink: 1 !important; } .flex-wrap { - flex-wrap: wrap !important; + flex-wrap: wrap !important; } .flex-nowrap { - flex-wrap: nowrap !important; + flex-wrap: nowrap !important; } .flex-wrap-reverse { - flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; } .justify-content-start { - justify-content: flex-start !important; + justify-content: flex-start !important; } .justify-content-end { - justify-content: flex-end !important; + justify-content: flex-end !important; } .justify-content-center { - justify-content: center !important; + justify-content: center !important; } .justify-content-between { - justify-content: space-between !important; + justify-content: space-between !important; } .justify-content-around { - justify-content: space-around !important; + justify-content: space-around !important; } .justify-content-evenly { - justify-content: space-evenly !important; + justify-content: space-evenly !important; } .align-items-start { - align-items: flex-start !important; + align-items: flex-start !important; } .align-items-end { - align-items: flex-end !important; + align-items: flex-end !important; } .align-items-center { - align-items: center !important; + align-items: center !important; } .align-items-baseline { - align-items: baseline !important; + align-items: baseline !important; } .align-items-stretch { - align-items: stretch !important; + align-items: stretch !important; } .align-content-start { - align-content: flex-start !important; + align-content: flex-start !important; } .align-content-end { - align-content: flex-end !important; + align-content: flex-end !important; } .align-content-center { - align-content: center !important; + align-content: center !important; } .align-content-between { - align-content: space-between !important; + align-content: space-between !important; } .align-content-around { - align-content: space-around !important; + align-content: space-around !important; } .align-content-stretch { - align-content: stretch !important; + align-content: stretch !important; } .align-self-auto { - align-self: auto !important; + align-self: auto !important; } .align-self-start { - align-self: flex-start !important; + align-self: flex-start !important; } .align-self-end { - align-self: flex-end !important; + align-self: flex-end !important; } .align-self-center { - align-self: center !important; + align-self: center !important; } .align-self-baseline { - align-self: baseline !important; + align-self: baseline !important; } .align-self-stretch { - align-self: stretch !important; + align-self: stretch !important; } .order-first { - order: -1 !important; + order: -1 !important; } .order-0 { - order: 0 !important; + order: 0 !important; } .order-1 { - order: 1 !important; + order: 1 !important; } .order-2 { - order: 2 !important; + order: 2 !important; } .order-3 { - order: 3 !important; + order: 3 !important; } .order-4 { - order: 4 !important; + order: 4 !important; } .order-5 { - order: 5 !important; + order: 5 !important; } .order-last { - order: 6 !important; + order: 6 !important; } .m-0 { - margin: 0 !important; + margin: 0 !important; } .m-1 { - margin: 0.25rem !important; + margin: 0.25rem !important; } .m-2 { - margin: 0.5rem !important; + margin: 0.5rem !important; } .m-3 { - margin: 1rem !important; + margin: 1rem !important; } .m-4 { - margin: 1.5rem !important; + margin: 1.5rem !important; } .m-5 { - margin: 3rem !important; + margin: 3rem !important; } .m-auto { - margin: auto !important; + margin: auto !important; } .mx-0 { - margin-left: 0 !important; - margin-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; } .mx-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; } .mx-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; } .mx-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; + margin-left: 1rem !important; + margin-right: 1rem !important; } .mx-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; } .mx-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; + margin-left: 3rem !important; + margin-right: 3rem !important; } .mx-auto { - margin-left: auto !important; - margin-right: auto !important; + margin-left: auto !important; + margin-right: auto !important; } .my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; } .my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } .my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } .my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; + margin-top: 1rem !important; + margin-bottom: 1rem !important; } .my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } .my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; + margin-top: 3rem !important; + margin-bottom: 3rem !important; } .my-auto { - margin-top: auto !important; - margin-bottom: auto !important; + margin-top: auto !important; + margin-bottom: auto !important; } .mt-0 { - margin-top: 0 !important; + margin-top: 0 !important; } .mt-1 { - margin-top: 0.25rem !important; + margin-top: 0.25rem !important; } .mt-2 { - margin-top: 0.5rem !important; + margin-top: 0.5rem !important; } .mt-3 { - margin-top: 1rem !important; + margin-top: 1rem !important; } .mt-4 { - margin-top: 1.5rem !important; + margin-top: 1.5rem !important; } .mt-5 { - margin-top: 3rem !important; + margin-top: 3rem !important; } .mt-auto { - margin-top: auto !important; + margin-top: auto !important; } .me-0 { - margin-left: 0 !important; + margin-left: 0 !important; } .me-1 { - margin-left: 0.25rem !important; + margin-left: 0.25rem !important; } .me-2 { - margin-left: 0.5rem !important; + margin-left: 0.5rem !important; } .me-3 { - margin-left: 1rem !important; + margin-left: 1rem !important; } .me-4 { - margin-left: 1.5rem !important; + margin-left: 1.5rem !important; } .me-5 { - margin-left: 3rem !important; + margin-left: 3rem !important; } .me-auto { - margin-left: auto !important; + margin-left: auto !important; } .mb-0 { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .mb-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 0.25rem !important; } .mb-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 0.5rem !important; } .mb-3 { - margin-bottom: 1rem !important; + margin-bottom: 1rem !important; } .mb-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 1.5rem !important; } .mb-5 { - margin-bottom: 3rem !important; + margin-bottom: 3rem !important; } .mb-auto { - margin-bottom: auto !important; + margin-bottom: auto !important; } .ms-0 { - margin-right: 0 !important; + margin-right: 0 !important; } .ms-1 { - margin-right: 0.25rem !important; + margin-right: 0.25rem !important; } .ms-2 { - margin-right: 0.5rem !important; + margin-right: 0.5rem !important; } .ms-3 { - margin-right: 1rem !important; + margin-right: 1rem !important; } .ms-4 { - margin-right: 1.5rem !important; + margin-right: 1.5rem !important; } .ms-5 { - margin-right: 3rem !important; + margin-right: 3rem !important; } .ms-auto { - margin-right: auto !important; + margin-right: auto !important; } .p-0 { - padding: 0 !important; + padding: 0 !important; } .p-1 { - padding: 0.25rem !important; + padding: 0.25rem !important; } .p-2 { - padding: 0.5rem !important; + padding: 0.5rem !important; } .p-3 { - padding: 1rem !important; + padding: 1rem !important; } .p-4 { - padding: 1.5rem !important; + padding: 1.5rem !important; } .p-5 { - padding: 3rem !important; + padding: 3rem !important; } .px-0 { - padding-left: 0 !important; - padding-right: 0 !important; + padding-left: 0 !important; + padding-right: 0 !important; } .px-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; } .px-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; } .px-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; + padding-left: 1rem !important; + padding-right: 1rem !important; } .px-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; } .px-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; + padding-left: 3rem !important; + padding-right: 3rem !important; } .py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; } .py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } .py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } .py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; + padding-top: 1rem !important; + padding-bottom: 1rem !important; } .py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } .py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; + padding-top: 3rem !important; + padding-bottom: 3rem !important; } .pt-0 { - padding-top: 0 !important; + padding-top: 0 !important; } .pt-1 { - padding-top: 0.25rem !important; + padding-top: 0.25rem !important; } .pt-2 { - padding-top: 0.5rem !important; + padding-top: 0.5rem !important; } .pt-3 { - padding-top: 1rem !important; + padding-top: 1rem !important; } .pt-4 { - padding-top: 1.5rem !important; + padding-top: 1.5rem !important; } .pt-5 { - padding-top: 3rem !important; + padding-top: 3rem !important; } .pe-0 { - padding-left: 0 !important; + padding-left: 0 !important; } .pe-1 { - padding-left: 0.25rem !important; + padding-left: 0.25rem !important; } .pe-2 { - padding-left: 0.5rem !important; + padding-left: 0.5rem !important; } .pe-3 { - padding-left: 1rem !important; + padding-left: 1rem !important; } .pe-4 { - padding-left: 1.5rem !important; + padding-left: 1.5rem !important; } .pe-5 { - padding-left: 3rem !important; + padding-left: 3rem !important; } .pb-0 { - padding-bottom: 0 !important; + padding-bottom: 0 !important; } .pb-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 0.25rem !important; } .pb-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 0.5rem !important; } .pb-3 { - padding-bottom: 1rem !important; + padding-bottom: 1rem !important; } .pb-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 1.5rem !important; } .pb-5 { - padding-bottom: 3rem !important; + padding-bottom: 3rem !important; } .ps-0 { - padding-right: 0 !important; + padding-right: 0 !important; } .ps-1 { - padding-right: 0.25rem !important; + padding-right: 0.25rem !important; } .ps-2 { - padding-right: 0.5rem !important; + padding-right: 0.5rem !important; } .ps-3 { - padding-right: 1rem !important; + padding-right: 1rem !important; } .ps-4 { - padding-right: 1.5rem !important; + padding-right: 1.5rem !important; } .ps-5 { - padding-right: 3rem !important; + padding-right: 3rem !important; } .gap-0 { - gap: 0 !important; + gap: 0 !important; } .gap-1 { - gap: 0.25rem !important; + gap: 0.25rem !important; } .gap-2 { - gap: 0.5rem !important; + gap: 0.5rem !important; } .gap-3 { - gap: 1rem !important; + gap: 1rem !important; } .gap-4 { - gap: 1.5rem !important; + gap: 1.5rem !important; } .gap-5 { - gap: 3rem !important; + gap: 3rem !important; } .row-gap-0 { - row-gap: 0 !important; + row-gap: 0 !important; } .row-gap-1 { - row-gap: 0.25rem !important; + row-gap: 0.25rem !important; } .row-gap-2 { - row-gap: 0.5rem !important; + row-gap: 0.5rem !important; } .row-gap-3 { - row-gap: 1rem !important; + row-gap: 1rem !important; } .row-gap-4 { - row-gap: 1.5rem !important; + row-gap: 1.5rem !important; } .row-gap-5 { - row-gap: 3rem !important; + row-gap: 3rem !important; } .column-gap-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; + -moz-column-gap: 0 !important; + column-gap: 0 !important; } .column-gap-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; } .column-gap-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; } .column-gap-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; } .column-gap-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; } .column-gap-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; } .font-monospace { - font-family: var(--bs-font-monospace) !important; + font-family: var(--bs-font-monospace) !important; } .fs-1 { - font-size: calc(1.375rem + 1.5vw) !important; + font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { - font-size: calc(1.325rem + 0.9vw) !important; + font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { - font-size: calc(1.3rem + 0.6vw) !important; + font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { - font-size: calc(1.275rem + 0.3vw) !important; + font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { - font-size: 1.25rem !important; + font-size: 1.25rem !important; } .fs-6 { - font-size: 1rem !important; + font-size: 1rem !important; } .fst-italic { - font-style: italic !important; + font-style: italic !important; } .fst-normal { - font-style: normal !important; + font-style: normal !important; } .fw-lighter { - font-weight: lighter !important; + font-weight: lighter !important; } .fw-light { - font-weight: 300 !important; + font-weight: 300 !important; } .fw-normal { - font-weight: 400 !important; + font-weight: 400 !important; } .fw-medium { - font-weight: 500 !important; + font-weight: 500 !important; } .fw-semibold { - font-weight: 600 !important; + font-weight: 600 !important; } .fw-bold { - font-weight: 700 !important; + font-weight: 700 !important; } .fw-bolder { - font-weight: bolder !important; + font-weight: bolder !important; } .lh-1 { - line-height: 1 !important; + line-height: 1 !important; } .lh-sm { - line-height: 1.25 !important; + line-height: 1.25 !important; } .lh-base { - line-height: 1.5 !important; + line-height: 1.5 !important; } .lh-lg { - line-height: 2 !important; + line-height: 2 !important; } .text-start { - text-align: right !important; + text-align: right !important; } .text-end { - text-align: left !important; + text-align: left !important; } .text-center { - text-align: center !important; + text-align: center !important; } .text-decoration-none { - text-decoration: none !important; + text-decoration: none !important; } .text-decoration-underline { - text-decoration: underline !important; + text-decoration: underline !important; } .text-decoration-line-through { - text-decoration: line-through !important; + text-decoration: line-through !important; } .text-lowercase { - text-transform: lowercase !important; + text-transform: lowercase !important; } .text-uppercase { - text-transform: uppercase !important; + text-transform: uppercase !important; } .text-capitalize { - text-transform: capitalize !important; + text-transform: capitalize !important; } .text-wrap { - white-space: normal !important; + white-space: normal !important; } .text-nowrap { - white-space: nowrap !important; + white-space: nowrap !important; } + .text-primary { - --bs-text-opacity: 1; - color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { - --bs-text-opacity: 1; - color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { - --bs-text-opacity: 1; - color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { - --bs-text-opacity: 1; - color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { - --bs-text-opacity: 1; - color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { - --bs-text-opacity: 1; - color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { - --bs-text-opacity: 1; - color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { - --bs-text-opacity: 1; - color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { - --bs-text-opacity: 1; - color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { - --bs-text-opacity: 1; - color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { - --bs-text-opacity: 1; - color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-black-50 { - --bs-text-opacity: 1; - color: rgba(0, 0, 0, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { - --bs-text-opacity: 1; - color: rgba(255, 255, 255, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(255, 255, 255, 0.5) !important; } .text-body-secondary { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-body-tertiary { - --bs-text-opacity: 1; - color: var(--bs-tertiary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-tertiary-color) !important; } .text-body-emphasis { - --bs-text-opacity: 1; - color: var(--bs-emphasis-color) !important; + --bs-text-opacity: 1; + color: var(--bs-emphasis-color) !important; } .text-reset { - --bs-text-opacity: 1; - color: inherit !important; + --bs-text-opacity: 1; + color: inherit !important; } .text-opacity-25 { - --bs-text-opacity: 0.25; + --bs-text-opacity: 0.25; } .text-opacity-50 { - --bs-text-opacity: 0.5; + --bs-text-opacity: 0.5; } .text-opacity-75 { - --bs-text-opacity: 0.75; + --bs-text-opacity: 0.75; } .text-opacity-100 { - --bs-text-opacity: 1; + --bs-text-opacity: 1; } .text-primary-emphasis { - color: var(--bs-primary-text-emphasis) !important; + color: var(--bs-primary-text-emphasis) !important; } .text-secondary-emphasis { - color: var(--bs-secondary-text-emphasis) !important; + color: var(--bs-secondary-text-emphasis) !important; } .text-success-emphasis { - color: var(--bs-success-text-emphasis) !important; + color: var(--bs-success-text-emphasis) !important; } .text-info-emphasis { - color: var(--bs-info-text-emphasis) !important; + color: var(--bs-info-text-emphasis) !important; } .text-warning-emphasis { - color: var(--bs-warning-text-emphasis) !important; + color: var(--bs-warning-text-emphasis) !important; } .text-danger-emphasis { - color: var(--bs-danger-text-emphasis) !important; + color: var(--bs-danger-text-emphasis) !important; } .text-light-emphasis { - color: var(--bs-light-text-emphasis) !important; + color: var(--bs-light-text-emphasis) !important; } .text-dark-emphasis { - color: var(--bs-dark-text-emphasis) !important; + color: var(--bs-dark-text-emphasis) !important; } .link-opacity-10 { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-10-hover:hover { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-25 { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-25-hover:hover { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-50 { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-50-hover:hover { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-75 { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-75-hover:hover { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-100 { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-opacity-100-hover:hover { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-offset-1 { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-1-hover:hover { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-2 { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-2-hover:hover { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-3 { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-offset-3-hover:hover { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-underline-primary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-secondary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-success { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-info { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-warning { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-danger { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-light { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-dark { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-underline-opacity-0 { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-0-hover:hover { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-10 { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-10-hover:hover { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-25 { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-25-hover:hover { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-50 { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-50-hover:hover { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-75 { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-75-hover:hover { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-100 { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .link-underline-opacity-100-hover:hover { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .bg-primary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; } .bg-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; } .bg-success { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; } .bg-info { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; } .bg-danger { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-transparent { - --bs-bg-opacity: 1; - background-color: transparent !important; + --bs-bg-opacity: 1; + background-color: transparent !important; } .bg-body-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-body-tertiary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-opacity-10 { - --bs-bg-opacity: 0.1; + --bs-bg-opacity: 0.1; } .bg-opacity-25 { - --bs-bg-opacity: 0.25; + --bs-bg-opacity: 0.25; } .bg-opacity-50 { - --bs-bg-opacity: 0.5; + --bs-bg-opacity: 0.5; } .bg-opacity-75 { - --bs-bg-opacity: 0.75; + --bs-bg-opacity: 0.75; } .bg-opacity-100 { - --bs-bg-opacity: 1; + --bs-bg-opacity: 1; } .bg-primary-subtle { - background-color: var(--bs-primary-bg-subtle) !important; + background-color: var(--bs-primary-bg-subtle) !important; } .bg-secondary-subtle { - background-color: var(--bs-secondary-bg-subtle) !important; + background-color: var(--bs-secondary-bg-subtle) !important; } .bg-success-subtle { - background-color: var(--bs-success-bg-subtle) !important; + background-color: var(--bs-success-bg-subtle) !important; } .bg-info-subtle { - background-color: var(--bs-info-bg-subtle) !important; + background-color: var(--bs-info-bg-subtle) !important; } .bg-warning-subtle { - background-color: var(--bs-warning-bg-subtle) !important; + background-color: var(--bs-warning-bg-subtle) !important; } .bg-danger-subtle { - background-color: var(--bs-danger-bg-subtle) !important; + background-color: var(--bs-danger-bg-subtle) !important; } .bg-light-subtle { - background-color: var(--bs-light-bg-subtle) !important; + background-color: var(--bs-light-bg-subtle) !important; } .bg-dark-subtle { - background-color: var(--bs-dark-bg-subtle) !important; + background-color: var(--bs-dark-bg-subtle) !important; } .bg-gradient { - background-image: var(--bs-gradient) !important; + background-image: var(--bs-gradient) !important; } .user-select-all { - -webkit-user-select: all !important; - -moz-user-select: all !important; - user-select: all !important; + -webkit-user-select: all !important; + -moz-user-select: all !important; + user-select: all !important; } .user-select-auto { - -webkit-user-select: auto !important; - -moz-user-select: auto !important; - user-select: auto !important; + -webkit-user-select: auto !important; + -moz-user-select: auto !important; + user-select: auto !important; } .user-select-none { - -webkit-user-select: none !important; - -moz-user-select: none !important; - user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + user-select: none !important; } .pe-none { - pointer-events: none !important; + pointer-events: none !important; } .pe-auto { - pointer-events: auto !important; + pointer-events: auto !important; } .rounded { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-0 { - border-radius: 0 !important; + border-radius: 0 !important; } .rounded-1 { - border-radius: var(--bs-border-radius-sm) !important; + border-radius: var(--bs-border-radius-sm) !important; } .rounded-2 { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-3 { - border-radius: var(--bs-border-radius-lg) !important; + border-radius: var(--bs-border-radius-lg) !important; } .rounded-4 { - border-radius: var(--bs-border-radius-xl) !important; + border-radius: var(--bs-border-radius-xl) !important; } .rounded-5 { - border-radius: var(--bs-border-radius-xxl) !important; + border-radius: var(--bs-border-radius-xxl) !important; } .rounded-circle { - border-radius: 50% !important; + border-radius: 50% !important; } .rounded-pill { - border-radius: var(--bs-border-radius-pill) !important; + border-radius: var(--bs-border-radius-pill) !important; } .rounded-top { - border-top-right-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-top-0 { - border-top-right-radius: 0 !important; - border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; } .rounded-top-1 { - border-top-right-radius: var(--bs-border-radius-sm) !important; - border-top-left-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; } .rounded-top-2 { - border-top-right-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-top-3 { - border-top-right-radius: var(--bs-border-radius-lg) !important; - border-top-left-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; } .rounded-top-4 { - border-top-right-radius: var(--bs-border-radius-xl) !important; - border-top-left-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; } .rounded-top-5 { - border-top-right-radius: var(--bs-border-radius-xxl) !important; - border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-top-circle { - border-top-right-radius: 50% !important; - border-top-left-radius: 50% !important; + border-top-right-radius: 50% !important; + border-top-left-radius: 50% !important; } .rounded-top-pill { - border-top-right-radius: var(--bs-border-radius-pill) !important; - border-top-left-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; } .rounded-end { - border-top-left-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-end-0 { - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; } .rounded-end-1 { - border-top-left-radius: var(--bs-border-radius-sm) !important; - border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; } .rounded-end-2 { - border-top-left-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-end-3 { - border-top-left-radius: var(--bs-border-radius-lg) !important; - border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; } .rounded-end-4 { - border-top-left-radius: var(--bs-border-radius-xl) !important; - border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; } .rounded-end-5 { - border-top-left-radius: var(--bs-border-radius-xxl) !important; - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-end-circle { - border-top-left-radius: 50% !important; - border-bottom-left-radius: 50% !important; + border-top-left-radius: 50% !important; + border-bottom-left-radius: 50% !important; } .rounded-end-pill { - border-top-left-radius: var(--bs-border-radius-pill) !important; - border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; } .rounded-bottom { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-bottom-0 { - border-bottom-left-radius: 0 !important; - border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; } .rounded-bottom-1 { - border-bottom-left-radius: var(--bs-border-radius-sm) !important; - border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; } .rounded-bottom-2 { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-bottom-3 { - border-bottom-left-radius: var(--bs-border-radius-lg) !important; - border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; } .rounded-bottom-4 { - border-bottom-left-radius: var(--bs-border-radius-xl) !important; - border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; } .rounded-bottom-5 { - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-bottom-circle { - border-bottom-left-radius: 50% !important; - border-bottom-right-radius: 50% !important; + border-bottom-left-radius: 50% !important; + border-bottom-right-radius: 50% !important; } .rounded-bottom-pill { - border-bottom-left-radius: var(--bs-border-radius-pill) !important; - border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; } .rounded-start { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-start-0 { - border-bottom-right-radius: 0 !important; - border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; + border-top-right-radius: 0 !important; } .rounded-start-1 { - border-bottom-right-radius: var(--bs-border-radius-sm) !important; - border-top-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; } .rounded-start-2 { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-start-3 { - border-bottom-right-radius: var(--bs-border-radius-lg) !important; - border-top-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; } .rounded-start-4 { - border-bottom-right-radius: var(--bs-border-radius-xl) !important; - border-top-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; } .rounded-start-5 { - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; - border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-start-circle { - border-bottom-right-radius: 50% !important; - border-top-right-radius: 50% !important; + border-bottom-right-radius: 50% !important; + border-top-right-radius: 50% !important; } .rounded-start-pill { - border-bottom-right-radius: var(--bs-border-radius-pill) !important; - border-top-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; } .visible { - visibility: visible !important; + visibility: visible !important; } .invisible { - visibility: hidden !important; + visibility: hidden !important; } .z-n1 { - z-index: -1 !important; + z-index: -1 !important; } .z-0 { - z-index: 0 !important; + z-index: 0 !important; } .z-1 { - z-index: 1 !important; + z-index: 1 !important; } .z-2 { - z-index: 2 !important; + z-index: 2 !important; } .z-3 { - z-index: 3 !important; + z-index: 3 !important; } @media (min-width: 576px) { - .float-sm-start { - float: right !important; - } - .float-sm-end { - float: left !important; - } - .float-sm-none { - float: none !important; - } - .object-fit-sm-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-sm-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-sm-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-sm-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-sm-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-sm-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-sm-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-sm-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-sm-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-sm-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-sm-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-left: 0 !important; - } - .me-sm-1 { - margin-left: 0.25rem !important; - } - .me-sm-2 { - margin-left: 0.5rem !important; - } - .me-sm-3 { - margin-left: 1rem !important; - } - .me-sm-4 { - margin-left: 1.5rem !important; - } - .me-sm-5 { - margin-left: 3rem !important; - } - .me-sm-auto { - margin-left: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-right: 0 !important; - } - .ms-sm-1 { - margin-right: 0.25rem !important; - } - .ms-sm-2 { - margin-right: 0.5rem !important; - } - .ms-sm-3 { - margin-right: 1rem !important; - } - .ms-sm-4 { - margin-right: 1.5rem !important; - } - .ms-sm-5 { - margin-right: 3rem !important; - } - .ms-sm-auto { - margin-right: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-sm-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-sm-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-sm-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-sm-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-sm-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-left: 0 !important; - } - .pe-sm-1 { - padding-left: 0.25rem !important; - } - .pe-sm-2 { - padding-left: 0.5rem !important; - } - .pe-sm-3 { - padding-left: 1rem !important; - } - .pe-sm-4 { - padding-left: 1.5rem !important; - } - .pe-sm-5 { - padding-left: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-right: 0 !important; - } - .ps-sm-1 { - padding-right: 0.25rem !important; - } - .ps-sm-2 { - padding-right: 0.5rem !important; - } - .ps-sm-3 { - padding-right: 1rem !important; - } - .ps-sm-4 { - padding-right: 1.5rem !important; - } - .ps-sm-5 { - padding-right: 3rem !important; - } - .gap-sm-0 { - gap: 0 !important; - } - .gap-sm-1 { - gap: 0.25rem !important; - } - .gap-sm-2 { - gap: 0.5rem !important; - } - .gap-sm-3 { - gap: 1rem !important; - } - .gap-sm-4 { - gap: 1.5rem !important; - } - .gap-sm-5 { - gap: 3rem !important; - } - .row-gap-sm-0 { - row-gap: 0 !important; - } - .row-gap-sm-1 { - row-gap: 0.25rem !important; - } - .row-gap-sm-2 { - row-gap: 0.5rem !important; - } - .row-gap-sm-3 { - row-gap: 1rem !important; - } - .row-gap-sm-4 { - row-gap: 1.5rem !important; - } - .row-gap-sm-5 { - row-gap: 3rem !important; - } - .column-gap-sm-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-sm-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-sm-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-sm-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-sm-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-sm-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-sm-start { - text-align: right !important; - } - .text-sm-end { - text-align: left !important; - } - .text-sm-center { - text-align: center !important; - } -} -@media (min-width: 768px) { - .float-md-start { - float: right !important; - } - .float-md-end { - float: left !important; - } - .float-md-none { - float: none !important; - } - .object-fit-md-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-md-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-md-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-md-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-md-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-md-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-md-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-md-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-md-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-md-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-md-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-left: 0 !important; - } - .me-md-1 { - margin-left: 0.25rem !important; - } - .me-md-2 { - margin-left: 0.5rem !important; - } - .me-md-3 { - margin-left: 1rem !important; - } - .me-md-4 { - margin-left: 1.5rem !important; - } - .me-md-5 { - margin-left: 3rem !important; - } - .me-md-auto { - margin-left: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-right: 0 !important; - } - .ms-md-1 { - margin-right: 0.25rem !important; - } - .ms-md-2 { - margin-right: 0.5rem !important; - } - .ms-md-3 { - margin-right: 1rem !important; - } - .ms-md-4 { - margin-right: 1.5rem !important; - } - .ms-md-5 { - margin-right: 3rem !important; - } - .ms-md-auto { - margin-right: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-md-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-md-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-md-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-md-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-md-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-left: 0 !important; - } - .pe-md-1 { - padding-left: 0.25rem !important; - } - .pe-md-2 { - padding-left: 0.5rem !important; - } - .pe-md-3 { - padding-left: 1rem !important; - } - .pe-md-4 { - padding-left: 1.5rem !important; - } - .pe-md-5 { - padding-left: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-right: 0 !important; - } - .ps-md-1 { - padding-right: 0.25rem !important; - } - .ps-md-2 { - padding-right: 0.5rem !important; - } - .ps-md-3 { - padding-right: 1rem !important; - } - .ps-md-4 { - padding-right: 1.5rem !important; - } - .ps-md-5 { - padding-right: 3rem !important; - } - .gap-md-0 { - gap: 0 !important; - } - .gap-md-1 { - gap: 0.25rem !important; - } - .gap-md-2 { - gap: 0.5rem !important; - } - .gap-md-3 { - gap: 1rem !important; - } - .gap-md-4 { - gap: 1.5rem !important; - } - .gap-md-5 { - gap: 3rem !important; - } - .row-gap-md-0 { - row-gap: 0 !important; - } - .row-gap-md-1 { - row-gap: 0.25rem !important; - } - .row-gap-md-2 { - row-gap: 0.5rem !important; - } - .row-gap-md-3 { - row-gap: 1rem !important; - } - .row-gap-md-4 { - row-gap: 1.5rem !important; - } - .row-gap-md-5 { - row-gap: 3rem !important; - } - .column-gap-md-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-md-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-md-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-md-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-md-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-md-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-md-start { - text-align: right !important; - } - .text-md-end { - text-align: left !important; - } - .text-md-center { - text-align: center !important; - } -} -@media (min-width: 992px) { - .float-lg-start { - float: right !important; - } - .float-lg-end { - float: left !important; - } - .float-lg-none { - float: none !important; - } - .object-fit-lg-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-lg-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-lg-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-lg-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-lg-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-lg-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-lg-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-lg-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-lg-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-lg-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-lg-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-left: 0 !important; - } - .me-lg-1 { - margin-left: 0.25rem !important; - } - .me-lg-2 { - margin-left: 0.5rem !important; - } - .me-lg-3 { - margin-left: 1rem !important; - } - .me-lg-4 { - margin-left: 1.5rem !important; - } - .me-lg-5 { - margin-left: 3rem !important; - } - .me-lg-auto { - margin-left: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-right: 0 !important; - } - .ms-lg-1 { - margin-right: 0.25rem !important; - } - .ms-lg-2 { - margin-right: 0.5rem !important; - } - .ms-lg-3 { - margin-right: 1rem !important; - } - .ms-lg-4 { - margin-right: 1.5rem !important; - } - .ms-lg-5 { - margin-right: 3rem !important; - } - .ms-lg-auto { - margin-right: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-lg-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-lg-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-lg-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-lg-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-lg-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-left: 0 !important; - } - .pe-lg-1 { - padding-left: 0.25rem !important; - } - .pe-lg-2 { - padding-left: 0.5rem !important; - } - .pe-lg-3 { - padding-left: 1rem !important; - } - .pe-lg-4 { - padding-left: 1.5rem !important; - } - .pe-lg-5 { - padding-left: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-right: 0 !important; - } - .ps-lg-1 { - padding-right: 0.25rem !important; - } - .ps-lg-2 { - padding-right: 0.5rem !important; - } - .ps-lg-3 { - padding-right: 1rem !important; - } - .ps-lg-4 { - padding-right: 1.5rem !important; - } - .ps-lg-5 { - padding-right: 3rem !important; - } - .gap-lg-0 { - gap: 0 !important; - } - .gap-lg-1 { - gap: 0.25rem !important; - } - .gap-lg-2 { - gap: 0.5rem !important; - } - .gap-lg-3 { - gap: 1rem !important; - } - .gap-lg-4 { - gap: 1.5rem !important; - } - .gap-lg-5 { - gap: 3rem !important; - } - .row-gap-lg-0 { - row-gap: 0 !important; - } - .row-gap-lg-1 { - row-gap: 0.25rem !important; - } - .row-gap-lg-2 { - row-gap: 0.5rem !important; - } - .row-gap-lg-3 { - row-gap: 1rem !important; - } - .row-gap-lg-4 { - row-gap: 1.5rem !important; - } - .row-gap-lg-5 { - row-gap: 3rem !important; - } - .column-gap-lg-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-lg-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-lg-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-lg-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-lg-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-lg-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-lg-start { - text-align: right !important; - } - .text-lg-end { - text-align: left !important; - } - .text-lg-center { - text-align: center !important; - } -} -@media (min-width: 1200px) { - .float-xl-start { - float: right !important; - } - .float-xl-end { - float: left !important; - } - .float-xl-none { - float: none !important; - } - .object-fit-xl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-left: 0 !important; - } - .me-xl-1 { - margin-left: 0.25rem !important; - } - .me-xl-2 { - margin-left: 0.5rem !important; - } - .me-xl-3 { - margin-left: 1rem !important; - } - .me-xl-4 { - margin-left: 1.5rem !important; - } - .me-xl-5 { - margin-left: 3rem !important; - } - .me-xl-auto { - margin-left: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-right: 0 !important; - } - .ms-xl-1 { - margin-right: 0.25rem !important; - } - .ms-xl-2 { - margin-right: 0.5rem !important; - } - .ms-xl-3 { - margin-right: 1rem !important; - } - .ms-xl-4 { - margin-right: 1.5rem !important; - } - .ms-xl-5 { - margin-right: 3rem !important; - } - .ms-xl-auto { - margin-right: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-left: 0 !important; - } - .pe-xl-1 { - padding-left: 0.25rem !important; - } - .pe-xl-2 { - padding-left: 0.5rem !important; - } - .pe-xl-3 { - padding-left: 1rem !important; - } - .pe-xl-4 { - padding-left: 1.5rem !important; - } - .pe-xl-5 { - padding-left: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-right: 0 !important; - } - .ps-xl-1 { - padding-right: 0.25rem !important; - } - .ps-xl-2 { - padding-right: 0.5rem !important; - } - .ps-xl-3 { - padding-right: 1rem !important; - } - .ps-xl-4 { - padding-right: 1.5rem !important; - } - .ps-xl-5 { - padding-right: 3rem !important; - } - .gap-xl-0 { - gap: 0 !important; - } - .gap-xl-1 { - gap: 0.25rem !important; - } - .gap-xl-2 { - gap: 0.5rem !important; - } - .gap-xl-3 { - gap: 1rem !important; - } - .gap-xl-4 { - gap: 1.5rem !important; - } - .gap-xl-5 { - gap: 3rem !important; - } - .row-gap-xl-0 { - row-gap: 0 !important; - } - .row-gap-xl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xl-3 { - row-gap: 1rem !important; - } - .row-gap-xl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xl-5 { - row-gap: 3rem !important; - } - .column-gap-xl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xl-start { - text-align: right !important; - } - .text-xl-end { - text-align: left !important; - } - .text-xl-center { - text-align: center !important; - } -} -@media (min-width: 1400px) { - .float-xxl-start { - float: right !important; - } - .float-xxl-end { - float: left !important; - } - .float-xxl-none { - float: none !important; - } - .object-fit-xxl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xxl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xxl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xxl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xxl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xxl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xxl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xxl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xxl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xxl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xxl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-left: 0 !important; - } - .me-xxl-1 { - margin-left: 0.25rem !important; - } - .me-xxl-2 { - margin-left: 0.5rem !important; - } - .me-xxl-3 { - margin-left: 1rem !important; - } - .me-xxl-4 { - margin-left: 1.5rem !important; - } - .me-xxl-5 { - margin-left: 3rem !important; - } - .me-xxl-auto { - margin-left: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-right: 0 !important; - } - .ms-xxl-1 { - margin-right: 0.25rem !important; - } - .ms-xxl-2 { - margin-right: 0.5rem !important; - } - .ms-xxl-3 { - margin-right: 1rem !important; - } - .ms-xxl-4 { - margin-right: 1.5rem !important; - } - .ms-xxl-5 { - margin-right: 3rem !important; - } - .ms-xxl-auto { - margin-right: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xxl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xxl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xxl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xxl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xxl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-left: 0 !important; - } - .pe-xxl-1 { - padding-left: 0.25rem !important; - } - .pe-xxl-2 { - padding-left: 0.5rem !important; - } - .pe-xxl-3 { - padding-left: 1rem !important; - } - .pe-xxl-4 { - padding-left: 1.5rem !important; - } - .pe-xxl-5 { - padding-left: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-right: 0 !important; - } - .ps-xxl-1 { - padding-right: 0.25rem !important; - } - .ps-xxl-2 { - padding-right: 0.5rem !important; - } - .ps-xxl-3 { - padding-right: 1rem !important; - } - .ps-xxl-4 { - padding-right: 1.5rem !important; - } - .ps-xxl-5 { - padding-right: 3rem !important; - } - .gap-xxl-0 { - gap: 0 !important; - } - .gap-xxl-1 { - gap: 0.25rem !important; - } - .gap-xxl-2 { - gap: 0.5rem !important; - } - .gap-xxl-3 { - gap: 1rem !important; - } - .gap-xxl-4 { - gap: 1.5rem !important; - } - .gap-xxl-5 { - gap: 3rem !important; - } - .row-gap-xxl-0 { - row-gap: 0 !important; - } - .row-gap-xxl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xxl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xxl-3 { - row-gap: 1rem !important; - } - .row-gap-xxl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xxl-5 { - row-gap: 3rem !important; - } - .column-gap-xxl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xxl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xxl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xxl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xxl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xxl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xxl-start { - text-align: right !important; - } - .text-xxl-end { - text-align: left !important; - } - .text-xxl-center { - text-align: center !important; - } + .float-sm-start { + float: right !important; + } + + .float-sm-end { + float: left !important; + } + + .float-sm-none { + float: none !important; + } + + .object-fit-sm-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-sm-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-sm-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-sm-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-sm-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-grid { + display: grid !important; + } + + .d-sm-inline-grid { + display: inline-grid !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: flex !important; + } + + .d-sm-inline-flex { + display: inline-flex !important; + } + + .d-sm-none { + display: none !important; + } + + .flex-sm-fill { + flex: 1 1 auto !important; + } + + .flex-sm-row { + flex-direction: row !important; + } + + .flex-sm-column { + flex-direction: column !important; + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-sm-wrap { + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-sm-start { + justify-content: flex-start !important; + } + + .justify-content-sm-end { + justify-content: flex-end !important; + } + + .justify-content-sm-center { + justify-content: center !important; + } + + .justify-content-sm-between { + justify-content: space-between !important; + } + + .justify-content-sm-around { + justify-content: space-around !important; + } + + .justify-content-sm-evenly { + justify-content: space-evenly !important; + } + + .align-items-sm-start { + align-items: flex-start !important; + } + + .align-items-sm-end { + align-items: flex-end !important; + } + + .align-items-sm-center { + align-items: center !important; + } + + .align-items-sm-baseline { + align-items: baseline !important; + } + + .align-items-sm-stretch { + align-items: stretch !important; + } + + .align-content-sm-start { + align-content: flex-start !important; + } + + .align-content-sm-end { + align-content: flex-end !important; + } + + .align-content-sm-center { + align-content: center !important; + } + + .align-content-sm-between { + align-content: space-between !important; + } + + .align-content-sm-around { + align-content: space-around !important; + } + + .align-content-sm-stretch { + align-content: stretch !important; + } + + .align-self-sm-auto { + align-self: auto !important; + } + + .align-self-sm-start { + align-self: flex-start !important; + } + + .align-self-sm-end { + align-self: flex-end !important; + } + + .align-self-sm-center { + align-self: center !important; + } + + .align-self-sm-baseline { + align-self: baseline !important; + } + + .align-self-sm-stretch { + align-self: stretch !important; + } + + .order-sm-first { + order: -1 !important; + } + + .order-sm-0 { + order: 0 !important; + } + + .order-sm-1 { + order: 1 !important; + } + + .order-sm-2 { + order: 2 !important; + } + + .order-sm-3 { + order: 3 !important; + } + + .order-sm-4 { + order: 4 !important; + } + + .order-sm-5 { + order: 5 !important; + } + + .order-sm-last { + order: 6 !important; + } + + .m-sm-0 { + margin: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mx-sm-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-sm-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-sm-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-sm-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-sm-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-sm-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-sm-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-sm-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-sm-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-sm-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-sm-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-sm-0 { + margin-top: 0 !important; + } + + .mt-sm-1 { + margin-top: 0.25rem !important; + } + + .mt-sm-2 { + margin-top: 0.5rem !important; + } + + .mt-sm-3 { + margin-top: 1rem !important; + } + + .mt-sm-4 { + margin-top: 1.5rem !important; + } + + .mt-sm-5 { + margin-top: 3rem !important; + } + + .mt-sm-auto { + margin-top: auto !important; + } + + .me-sm-0 { + margin-left: 0 !important; + } + + .me-sm-1 { + margin-left: 0.25rem !important; + } + + .me-sm-2 { + margin-left: 0.5rem !important; + } + + .me-sm-3 { + margin-left: 1rem !important; + } + + .me-sm-4 { + margin-left: 1.5rem !important; + } + + .me-sm-5 { + margin-left: 3rem !important; + } + + .me-sm-auto { + margin-left: auto !important; + } + + .mb-sm-0 { + margin-bottom: 0 !important; + } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; + } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; + } + + .mb-sm-3 { + margin-bottom: 1rem !important; + } + + .mb-sm-4 { + margin-bottom: 1.5rem !important; + } + + .mb-sm-5 { + margin-bottom: 3rem !important; + } + + .mb-sm-auto { + margin-bottom: auto !important; + } + + .ms-sm-0 { + margin-right: 0 !important; + } + + .ms-sm-1 { + margin-right: 0.25rem !important; + } + + .ms-sm-2 { + margin-right: 0.5rem !important; + } + + .ms-sm-3 { + margin-right: 1rem !important; + } + + .ms-sm-4 { + margin-right: 1.5rem !important; + } + + .ms-sm-5 { + margin-right: 3rem !important; + } + + .ms-sm-auto { + margin-right: auto !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .px-sm-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-sm-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-sm-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-sm-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-sm-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-sm-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-sm-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-sm-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-sm-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-sm-0 { + padding-top: 0 !important; + } + + .pt-sm-1 { + padding-top: 0.25rem !important; + } + + .pt-sm-2 { + padding-top: 0.5rem !important; + } + + .pt-sm-3 { + padding-top: 1rem !important; + } + + .pt-sm-4 { + padding-top: 1.5rem !important; + } + + .pt-sm-5 { + padding-top: 3rem !important; + } + + .pe-sm-0 { + padding-left: 0 !important; + } + + .pe-sm-1 { + padding-left: 0.25rem !important; + } + + .pe-sm-2 { + padding-left: 0.5rem !important; + } + + .pe-sm-3 { + padding-left: 1rem !important; + } + + .pe-sm-4 { + padding-left: 1.5rem !important; + } + + .pe-sm-5 { + padding-left: 3rem !important; + } + + .pb-sm-0 { + padding-bottom: 0 !important; + } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pb-sm-3 { + padding-bottom: 1rem !important; + } + + .pb-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pb-sm-5 { + padding-bottom: 3rem !important; + } + + .ps-sm-0 { + padding-right: 0 !important; + } + + .ps-sm-1 { + padding-right: 0.25rem !important; + } + + .ps-sm-2 { + padding-right: 0.5rem !important; + } + + .ps-sm-3 { + padding-right: 1rem !important; + } + + .ps-sm-4 { + padding-right: 1.5rem !important; + } + + .ps-sm-5 { + padding-right: 3rem !important; + } + + .gap-sm-0 { + gap: 0 !important; + } + + .gap-sm-1 { + gap: 0.25rem !important; + } + + .gap-sm-2 { + gap: 0.5rem !important; + } + + .gap-sm-3 { + gap: 1rem !important; + } + + .gap-sm-4 { + gap: 1.5rem !important; + } + + .gap-sm-5 { + gap: 3rem !important; + } + + .row-gap-sm-0 { + row-gap: 0 !important; + } + + .row-gap-sm-1 { + row-gap: 0.25rem !important; + } + + .row-gap-sm-2 { + row-gap: 0.5rem !important; + } + + .row-gap-sm-3 { + row-gap: 1rem !important; + } + + .row-gap-sm-4 { + row-gap: 1.5rem !important; + } + + .row-gap-sm-5 { + row-gap: 3rem !important; + } + + .column-gap-sm-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-sm-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-sm-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-sm-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-sm-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-sm-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-sm-start { + text-align: right !important; + } + + .text-sm-end { + text-align: left !important; + } + + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .float-md-start { + float: right !important; + } + + .float-md-end { + float: left !important; + } + + .float-md-none { + float: none !important; + } + + .object-fit-md-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-md-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-md-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-md-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-md-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-grid { + display: grid !important; + } + + .d-md-inline-grid { + display: inline-grid !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: flex !important; + } + + .d-md-inline-flex { + display: inline-flex !important; + } + + .d-md-none { + display: none !important; + } + + .flex-md-fill { + flex: 1 1 auto !important; + } + + .flex-md-row { + flex-direction: row !important; + } + + .flex-md-column { + flex-direction: column !important; + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-md-grow-0 { + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-md-wrap { + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-md-start { + justify-content: flex-start !important; + } + + .justify-content-md-end { + justify-content: flex-end !important; + } + + .justify-content-md-center { + justify-content: center !important; + } + + .justify-content-md-between { + justify-content: space-between !important; + } + + .justify-content-md-around { + justify-content: space-around !important; + } + + .justify-content-md-evenly { + justify-content: space-evenly !important; + } + + .align-items-md-start { + align-items: flex-start !important; + } + + .align-items-md-end { + align-items: flex-end !important; + } + + .align-items-md-center { + align-items: center !important; + } + + .align-items-md-baseline { + align-items: baseline !important; + } + + .align-items-md-stretch { + align-items: stretch !important; + } + + .align-content-md-start { + align-content: flex-start !important; + } + + .align-content-md-end { + align-content: flex-end !important; + } + + .align-content-md-center { + align-content: center !important; + } + + .align-content-md-between { + align-content: space-between !important; + } + + .align-content-md-around { + align-content: space-around !important; + } + + .align-content-md-stretch { + align-content: stretch !important; + } + + .align-self-md-auto { + align-self: auto !important; + } + + .align-self-md-start { + align-self: flex-start !important; + } + + .align-self-md-end { + align-self: flex-end !important; + } + + .align-self-md-center { + align-self: center !important; + } + + .align-self-md-baseline { + align-self: baseline !important; + } + + .align-self-md-stretch { + align-self: stretch !important; + } + + .order-md-first { + order: -1 !important; + } + + .order-md-0 { + order: 0 !important; + } + + .order-md-1 { + order: 1 !important; + } + + .order-md-2 { + order: 2 !important; + } + + .order-md-3 { + order: 3 !important; + } + + .order-md-4 { + order: 4 !important; + } + + .order-md-5 { + order: 5 !important; + } + + .order-md-last { + order: 6 !important; + } + + .m-md-0 { + margin: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mx-md-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-md-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-md-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-md-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-md-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-md-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-md-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-md-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-md-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-md-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-md-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-md-0 { + margin-top: 0 !important; + } + + .mt-md-1 { + margin-top: 0.25rem !important; + } + + .mt-md-2 { + margin-top: 0.5rem !important; + } + + .mt-md-3 { + margin-top: 1rem !important; + } + + .mt-md-4 { + margin-top: 1.5rem !important; + } + + .mt-md-5 { + margin-top: 3rem !important; + } + + .mt-md-auto { + margin-top: auto !important; + } + + .me-md-0 { + margin-left: 0 !important; + } + + .me-md-1 { + margin-left: 0.25rem !important; + } + + .me-md-2 { + margin-left: 0.5rem !important; + } + + .me-md-3 { + margin-left: 1rem !important; + } + + .me-md-4 { + margin-left: 1.5rem !important; + } + + .me-md-5 { + margin-left: 3rem !important; + } + + .me-md-auto { + margin-left: auto !important; + } + + .mb-md-0 { + margin-bottom: 0 !important; + } + + .mb-md-1 { + margin-bottom: 0.25rem !important; + } + + .mb-md-2 { + margin-bottom: 0.5rem !important; + } + + .mb-md-3 { + margin-bottom: 1rem !important; + } + + .mb-md-4 { + margin-bottom: 1.5rem !important; + } + + .mb-md-5 { + margin-bottom: 3rem !important; + } + + .mb-md-auto { + margin-bottom: auto !important; + } + + .ms-md-0 { + margin-right: 0 !important; + } + + .ms-md-1 { + margin-right: 0.25rem !important; + } + + .ms-md-2 { + margin-right: 0.5rem !important; + } + + .ms-md-3 { + margin-right: 1rem !important; + } + + .ms-md-4 { + margin-right: 1.5rem !important; + } + + .ms-md-5 { + margin-right: 3rem !important; + } + + .ms-md-auto { + margin-right: auto !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .px-md-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-md-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-md-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-md-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-md-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-md-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-md-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-md-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-md-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-md-0 { + padding-top: 0 !important; + } + + .pt-md-1 { + padding-top: 0.25rem !important; + } + + .pt-md-2 { + padding-top: 0.5rem !important; + } + + .pt-md-3 { + padding-top: 1rem !important; + } + + .pt-md-4 { + padding-top: 1.5rem !important; + } + + .pt-md-5 { + padding-top: 3rem !important; + } + + .pe-md-0 { + padding-left: 0 !important; + } + + .pe-md-1 { + padding-left: 0.25rem !important; + } + + .pe-md-2 { + padding-left: 0.5rem !important; + } + + .pe-md-3 { + padding-left: 1rem !important; + } + + .pe-md-4 { + padding-left: 1.5rem !important; + } + + .pe-md-5 { + padding-left: 3rem !important; + } + + .pb-md-0 { + padding-bottom: 0 !important; + } + + .pb-md-1 { + padding-bottom: 0.25rem !important; + } + + .pb-md-2 { + padding-bottom: 0.5rem !important; + } + + .pb-md-3 { + padding-bottom: 1rem !important; + } + + .pb-md-4 { + padding-bottom: 1.5rem !important; + } + + .pb-md-5 { + padding-bottom: 3rem !important; + } + + .ps-md-0 { + padding-right: 0 !important; + } + + .ps-md-1 { + padding-right: 0.25rem !important; + } + + .ps-md-2 { + padding-right: 0.5rem !important; + } + + .ps-md-3 { + padding-right: 1rem !important; + } + + .ps-md-4 { + padding-right: 1.5rem !important; + } + + .ps-md-5 { + padding-right: 3rem !important; + } + + .gap-md-0 { + gap: 0 !important; + } + + .gap-md-1 { + gap: 0.25rem !important; + } + + .gap-md-2 { + gap: 0.5rem !important; + } + + .gap-md-3 { + gap: 1rem !important; + } + + .gap-md-4 { + gap: 1.5rem !important; + } + + .gap-md-5 { + gap: 3rem !important; + } + + .row-gap-md-0 { + row-gap: 0 !important; + } + + .row-gap-md-1 { + row-gap: 0.25rem !important; + } + + .row-gap-md-2 { + row-gap: 0.5rem !important; + } + + .row-gap-md-3 { + row-gap: 1rem !important; + } + + .row-gap-md-4 { + row-gap: 1.5rem !important; + } + + .row-gap-md-5 { + row-gap: 3rem !important; + } + + .column-gap-md-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-md-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-md-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-md-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-md-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-md-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-md-start { + text-align: right !important; + } + + .text-md-end { + text-align: left !important; + } + + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .float-lg-start { + float: right !important; + } + + .float-lg-end { + float: left !important; + } + + .float-lg-none { + float: none !important; + } + + .object-fit-lg-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-lg-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-lg-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-lg-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-lg-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-grid { + display: grid !important; + } + + .d-lg-inline-grid { + display: inline-grid !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: flex !important; + } + + .d-lg-inline-flex { + display: inline-flex !important; + } + + .d-lg-none { + display: none !important; + } + + .flex-lg-fill { + flex: 1 1 auto !important; + } + + .flex-lg-row { + flex-direction: row !important; + } + + .flex-lg-column { + flex-direction: column !important; + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-lg-wrap { + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-lg-start { + justify-content: flex-start !important; + } + + .justify-content-lg-end { + justify-content: flex-end !important; + } + + .justify-content-lg-center { + justify-content: center !important; + } + + .justify-content-lg-between { + justify-content: space-between !important; + } + + .justify-content-lg-around { + justify-content: space-around !important; + } + + .justify-content-lg-evenly { + justify-content: space-evenly !important; + } + + .align-items-lg-start { + align-items: flex-start !important; + } + + .align-items-lg-end { + align-items: flex-end !important; + } + + .align-items-lg-center { + align-items: center !important; + } + + .align-items-lg-baseline { + align-items: baseline !important; + } + + .align-items-lg-stretch { + align-items: stretch !important; + } + + .align-content-lg-start { + align-content: flex-start !important; + } + + .align-content-lg-end { + align-content: flex-end !important; + } + + .align-content-lg-center { + align-content: center !important; + } + + .align-content-lg-between { + align-content: space-between !important; + } + + .align-content-lg-around { + align-content: space-around !important; + } + + .align-content-lg-stretch { + align-content: stretch !important; + } + + .align-self-lg-auto { + align-self: auto !important; + } + + .align-self-lg-start { + align-self: flex-start !important; + } + + .align-self-lg-end { + align-self: flex-end !important; + } + + .align-self-lg-center { + align-self: center !important; + } + + .align-self-lg-baseline { + align-self: baseline !important; + } + + .align-self-lg-stretch { + align-self: stretch !important; + } + + .order-lg-first { + order: -1 !important; + } + + .order-lg-0 { + order: 0 !important; + } + + .order-lg-1 { + order: 1 !important; + } + + .order-lg-2 { + order: 2 !important; + } + + .order-lg-3 { + order: 3 !important; + } + + .order-lg-4 { + order: 4 !important; + } + + .order-lg-5 { + order: 5 !important; + } + + .order-lg-last { + order: 6 !important; + } + + .m-lg-0 { + margin: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mx-lg-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-lg-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-lg-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-lg-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-lg-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-lg-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-lg-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-lg-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-lg-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-lg-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-lg-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-lg-0 { + margin-top: 0 !important; + } + + .mt-lg-1 { + margin-top: 0.25rem !important; + } + + .mt-lg-2 { + margin-top: 0.5rem !important; + } + + .mt-lg-3 { + margin-top: 1rem !important; + } + + .mt-lg-4 { + margin-top: 1.5rem !important; + } + + .mt-lg-5 { + margin-top: 3rem !important; + } + + .mt-lg-auto { + margin-top: auto !important; + } + + .me-lg-0 { + margin-left: 0 !important; + } + + .me-lg-1 { + margin-left: 0.25rem !important; + } + + .me-lg-2 { + margin-left: 0.5rem !important; + } + + .me-lg-3 { + margin-left: 1rem !important; + } + + .me-lg-4 { + margin-left: 1.5rem !important; + } + + .me-lg-5 { + margin-left: 3rem !important; + } + + .me-lg-auto { + margin-left: auto !important; + } + + .mb-lg-0 { + margin-bottom: 0 !important; + } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; + } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; + } + + .mb-lg-3 { + margin-bottom: 1rem !important; + } + + .mb-lg-4 { + margin-bottom: 1.5rem !important; + } + + .mb-lg-5 { + margin-bottom: 3rem !important; + } + + .mb-lg-auto { + margin-bottom: auto !important; + } + + .ms-lg-0 { + margin-right: 0 !important; + } + + .ms-lg-1 { + margin-right: 0.25rem !important; + } + + .ms-lg-2 { + margin-right: 0.5rem !important; + } + + .ms-lg-3 { + margin-right: 1rem !important; + } + + .ms-lg-4 { + margin-right: 1.5rem !important; + } + + .ms-lg-5 { + margin-right: 3rem !important; + } + + .ms-lg-auto { + margin-right: auto !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .px-lg-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-lg-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-lg-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-lg-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-lg-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-lg-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-lg-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-lg-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-lg-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-lg-0 { + padding-top: 0 !important; + } + + .pt-lg-1 { + padding-top: 0.25rem !important; + } + + .pt-lg-2 { + padding-top: 0.5rem !important; + } + + .pt-lg-3 { + padding-top: 1rem !important; + } + + .pt-lg-4 { + padding-top: 1.5rem !important; + } + + .pt-lg-5 { + padding-top: 3rem !important; + } + + .pe-lg-0 { + padding-left: 0 !important; + } + + .pe-lg-1 { + padding-left: 0.25rem !important; + } + + .pe-lg-2 { + padding-left: 0.5rem !important; + } + + .pe-lg-3 { + padding-left: 1rem !important; + } + + .pe-lg-4 { + padding-left: 1.5rem !important; + } + + .pe-lg-5 { + padding-left: 3rem !important; + } + + .pb-lg-0 { + padding-bottom: 0 !important; + } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pb-lg-3 { + padding-bottom: 1rem !important; + } + + .pb-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pb-lg-5 { + padding-bottom: 3rem !important; + } + + .ps-lg-0 { + padding-right: 0 !important; + } + + .ps-lg-1 { + padding-right: 0.25rem !important; + } + + .ps-lg-2 { + padding-right: 0.5rem !important; + } + + .ps-lg-3 { + padding-right: 1rem !important; + } + + .ps-lg-4 { + padding-right: 1.5rem !important; + } + + .ps-lg-5 { + padding-right: 3rem !important; + } + + .gap-lg-0 { + gap: 0 !important; + } + + .gap-lg-1 { + gap: 0.25rem !important; + } + + .gap-lg-2 { + gap: 0.5rem !important; + } + + .gap-lg-3 { + gap: 1rem !important; + } + + .gap-lg-4 { + gap: 1.5rem !important; + } + + .gap-lg-5 { + gap: 3rem !important; + } + + .row-gap-lg-0 { + row-gap: 0 !important; + } + + .row-gap-lg-1 { + row-gap: 0.25rem !important; + } + + .row-gap-lg-2 { + row-gap: 0.5rem !important; + } + + .row-gap-lg-3 { + row-gap: 1rem !important; + } + + .row-gap-lg-4 { + row-gap: 1.5rem !important; + } + + .row-gap-lg-5 { + row-gap: 3rem !important; + } + + .column-gap-lg-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-lg-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-lg-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-lg-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-lg-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-lg-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-lg-start { + text-align: right !important; + } + + .text-lg-end { + text-align: left !important; + } + + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .float-xl-start { + float: right !important; + } + + .float-xl-end { + float: left !important; + } + + .float-xl-none { + float: none !important; + } + + .object-fit-xl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-grid { + display: grid !important; + } + + .d-xl-inline-grid { + display: inline-grid !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: flex !important; + } + + .d-xl-inline-flex { + display: inline-flex !important; + } + + .d-xl-none { + display: none !important; + } + + .flex-xl-fill { + flex: 1 1 auto !important; + } + + .flex-xl-row { + flex-direction: row !important; + } + + .flex-xl-column { + flex-direction: column !important; + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xl-wrap { + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xl-start { + justify-content: flex-start !important; + } + + .justify-content-xl-end { + justify-content: flex-end !important; + } + + .justify-content-xl-center { + justify-content: center !important; + } + + .justify-content-xl-between { + justify-content: space-between !important; + } + + .justify-content-xl-around { + justify-content: space-around !important; + } + + .justify-content-xl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xl-start { + align-items: flex-start !important; + } + + .align-items-xl-end { + align-items: flex-end !important; + } + + .align-items-xl-center { + align-items: center !important; + } + + .align-items-xl-baseline { + align-items: baseline !important; + } + + .align-items-xl-stretch { + align-items: stretch !important; + } + + .align-content-xl-start { + align-content: flex-start !important; + } + + .align-content-xl-end { + align-content: flex-end !important; + } + + .align-content-xl-center { + align-content: center !important; + } + + .align-content-xl-between { + align-content: space-between !important; + } + + .align-content-xl-around { + align-content: space-around !important; + } + + .align-content-xl-stretch { + align-content: stretch !important; + } + + .align-self-xl-auto { + align-self: auto !important; + } + + .align-self-xl-start { + align-self: flex-start !important; + } + + .align-self-xl-end { + align-self: flex-end !important; + } + + .align-self-xl-center { + align-self: center !important; + } + + .align-self-xl-baseline { + align-self: baseline !important; + } + + .align-self-xl-stretch { + align-self: stretch !important; + } + + .order-xl-first { + order: -1 !important; + } + + .order-xl-0 { + order: 0 !important; + } + + .order-xl-1 { + order: 1 !important; + } + + .order-xl-2 { + order: 2 !important; + } + + .order-xl-3 { + order: 3 !important; + } + + .order-xl-4 { + order: 4 !important; + } + + .order-xl-5 { + order: 5 !important; + } + + .order-xl-last { + order: 6 !important; + } + + .m-xl-0 { + margin: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mx-xl-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-xl-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-xl-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-xl-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-xl-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-xl-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-xl-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xl-0 { + margin-top: 0 !important; + } + + .mt-xl-1 { + margin-top: 0.25rem !important; + } + + .mt-xl-2 { + margin-top: 0.5rem !important; + } + + .mt-xl-3 { + margin-top: 1rem !important; + } + + .mt-xl-4 { + margin-top: 1.5rem !important; + } + + .mt-xl-5 { + margin-top: 3rem !important; + } + + .mt-xl-auto { + margin-top: auto !important; + } + + .me-xl-0 { + margin-left: 0 !important; + } + + .me-xl-1 { + margin-left: 0.25rem !important; + } + + .me-xl-2 { + margin-left: 0.5rem !important; + } + + .me-xl-3 { + margin-left: 1rem !important; + } + + .me-xl-4 { + margin-left: 1.5rem !important; + } + + .me-xl-5 { + margin-left: 3rem !important; + } + + .me-xl-auto { + margin-left: auto !important; + } + + .mb-xl-0 { + margin-bottom: 0 !important; + } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xl-3 { + margin-bottom: 1rem !important; + } + + .mb-xl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xl-5 { + margin-bottom: 3rem !important; + } + + .mb-xl-auto { + margin-bottom: auto !important; + } + + .ms-xl-0 { + margin-right: 0 !important; + } + + .ms-xl-1 { + margin-right: 0.25rem !important; + } + + .ms-xl-2 { + margin-right: 0.5rem !important; + } + + .ms-xl-3 { + margin-right: 1rem !important; + } + + .ms-xl-4 { + margin-right: 1.5rem !important; + } + + .ms-xl-5 { + margin-right: 3rem !important; + } + + .ms-xl-auto { + margin-right: auto !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .px-xl-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-xl-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-xl-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-xl-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-xl-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-xl-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xl-0 { + padding-top: 0 !important; + } + + .pt-xl-1 { + padding-top: 0.25rem !important; + } + + .pt-xl-2 { + padding-top: 0.5rem !important; + } + + .pt-xl-3 { + padding-top: 1rem !important; + } + + .pt-xl-4 { + padding-top: 1.5rem !important; + } + + .pt-xl-5 { + padding-top: 3rem !important; + } + + .pe-xl-0 { + padding-left: 0 !important; + } + + .pe-xl-1 { + padding-left: 0.25rem !important; + } + + .pe-xl-2 { + padding-left: 0.5rem !important; + } + + .pe-xl-3 { + padding-left: 1rem !important; + } + + .pe-xl-4 { + padding-left: 1.5rem !important; + } + + .pe-xl-5 { + padding-left: 3rem !important; + } + + .pb-xl-0 { + padding-bottom: 0 !important; + } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xl-3 { + padding-bottom: 1rem !important; + } + + .pb-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xl-5 { + padding-bottom: 3rem !important; + } + + .ps-xl-0 { + padding-right: 0 !important; + } + + .ps-xl-1 { + padding-right: 0.25rem !important; + } + + .ps-xl-2 { + padding-right: 0.5rem !important; + } + + .ps-xl-3 { + padding-right: 1rem !important; + } + + .ps-xl-4 { + padding-right: 1.5rem !important; + } + + .ps-xl-5 { + padding-right: 3rem !important; + } + + .gap-xl-0 { + gap: 0 !important; + } + + .gap-xl-1 { + gap: 0.25rem !important; + } + + .gap-xl-2 { + gap: 0.5rem !important; + } + + .gap-xl-3 { + gap: 1rem !important; + } + + .gap-xl-4 { + gap: 1.5rem !important; + } + + .gap-xl-5 { + gap: 3rem !important; + } + + .row-gap-xl-0 { + row-gap: 0 !important; + } + + .row-gap-xl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xl-3 { + row-gap: 1rem !important; + } + + .row-gap-xl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xl-5 { + row-gap: 3rem !important; + } + + .column-gap-xl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xl-start { + text-align: right !important; + } + + .text-xl-end { + text-align: left !important; + } + + .text-xl-center { + text-align: center !important; + } +} + +@media (min-width: 1400px) { + .float-xxl-start { + float: right !important; + } + + .float-xxl-end { + float: left !important; + } + + .float-xxl-none { + float: none !important; + } + + .object-fit-xxl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xxl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xxl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xxl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xxl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xxl-inline { + display: inline !important; + } + + .d-xxl-inline-block { + display: inline-block !important; + } + + .d-xxl-block { + display: block !important; + } + + .d-xxl-grid { + display: grid !important; + } + + .d-xxl-inline-grid { + display: inline-grid !important; + } + + .d-xxl-table { + display: table !important; + } + + .d-xxl-table-row { + display: table-row !important; + } + + .d-xxl-table-cell { + display: table-cell !important; + } + + .d-xxl-flex { + display: flex !important; + } + + .d-xxl-inline-flex { + display: inline-flex !important; + } + + .d-xxl-none { + display: none !important; + } + + .flex-xxl-fill { + flex: 1 1 auto !important; + } + + .flex-xxl-row { + flex-direction: row !important; + } + + .flex-xxl-column { + flex-direction: column !important; + } + + .flex-xxl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xxl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xxl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xxl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xxl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xxl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xxl-wrap { + flex-wrap: wrap !important; + } + + .flex-xxl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xxl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xxl-start { + justify-content: flex-start !important; + } + + .justify-content-xxl-end { + justify-content: flex-end !important; + } + + .justify-content-xxl-center { + justify-content: center !important; + } + + .justify-content-xxl-between { + justify-content: space-between !important; + } + + .justify-content-xxl-around { + justify-content: space-around !important; + } + + .justify-content-xxl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xxl-start { + align-items: flex-start !important; + } + + .align-items-xxl-end { + align-items: flex-end !important; + } + + .align-items-xxl-center { + align-items: center !important; + } + + .align-items-xxl-baseline { + align-items: baseline !important; + } + + .align-items-xxl-stretch { + align-items: stretch !important; + } + + .align-content-xxl-start { + align-content: flex-start !important; + } + + .align-content-xxl-end { + align-content: flex-end !important; + } + + .align-content-xxl-center { + align-content: center !important; + } + + .align-content-xxl-between { + align-content: space-between !important; + } + + .align-content-xxl-around { + align-content: space-around !important; + } + + .align-content-xxl-stretch { + align-content: stretch !important; + } + + .align-self-xxl-auto { + align-self: auto !important; + } + + .align-self-xxl-start { + align-self: flex-start !important; + } + + .align-self-xxl-end { + align-self: flex-end !important; + } + + .align-self-xxl-center { + align-self: center !important; + } + + .align-self-xxl-baseline { + align-self: baseline !important; + } + + .align-self-xxl-stretch { + align-self: stretch !important; + } + + .order-xxl-first { + order: -1 !important; + } + + .order-xxl-0 { + order: 0 !important; + } + + .order-xxl-1 { + order: 1 !important; + } + + .order-xxl-2 { + order: 2 !important; + } + + .order-xxl-3 { + order: 3 !important; + } + + .order-xxl-4 { + order: 4 !important; + } + + .order-xxl-5 { + order: 5 !important; + } + + .order-xxl-last { + order: 6 !important; + } + + .m-xxl-0 { + margin: 0 !important; + } + + .m-xxl-1 { + margin: 0.25rem !important; + } + + .m-xxl-2 { + margin: 0.5rem !important; + } + + .m-xxl-3 { + margin: 1rem !important; + } + + .m-xxl-4 { + margin: 1.5rem !important; + } + + .m-xxl-5 { + margin: 3rem !important; + } + + .m-xxl-auto { + margin: auto !important; + } + + .mx-xxl-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-xxl-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-xxl-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-xxl-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-xxl-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-xxl-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-xxl-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-xxl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xxl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xxl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xxl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xxl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xxl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xxl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xxl-0 { + margin-top: 0 !important; + } + + .mt-xxl-1 { + margin-top: 0.25rem !important; + } + + .mt-xxl-2 { + margin-top: 0.5rem !important; + } + + .mt-xxl-3 { + margin-top: 1rem !important; + } + + .mt-xxl-4 { + margin-top: 1.5rem !important; + } + + .mt-xxl-5 { + margin-top: 3rem !important; + } + + .mt-xxl-auto { + margin-top: auto !important; + } + + .me-xxl-0 { + margin-left: 0 !important; + } + + .me-xxl-1 { + margin-left: 0.25rem !important; + } + + .me-xxl-2 { + margin-left: 0.5rem !important; + } + + .me-xxl-3 { + margin-left: 1rem !important; + } + + .me-xxl-4 { + margin-left: 1.5rem !important; + } + + .me-xxl-5 { + margin-left: 3rem !important; + } + + .me-xxl-auto { + margin-left: auto !important; + } + + .mb-xxl-0 { + margin-bottom: 0 !important; + } + + .mb-xxl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xxl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xxl-3 { + margin-bottom: 1rem !important; + } + + .mb-xxl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xxl-5 { + margin-bottom: 3rem !important; + } + + .mb-xxl-auto { + margin-bottom: auto !important; + } + + .ms-xxl-0 { + margin-right: 0 !important; + } + + .ms-xxl-1 { + margin-right: 0.25rem !important; + } + + .ms-xxl-2 { + margin-right: 0.5rem !important; + } + + .ms-xxl-3 { + margin-right: 1rem !important; + } + + .ms-xxl-4 { + margin-right: 1.5rem !important; + } + + .ms-xxl-5 { + margin-right: 3rem !important; + } + + .ms-xxl-auto { + margin-right: auto !important; + } + + .p-xxl-0 { + padding: 0 !important; + } + + .p-xxl-1 { + padding: 0.25rem !important; + } + + .p-xxl-2 { + padding: 0.5rem !important; + } + + .p-xxl-3 { + padding: 1rem !important; + } + + .p-xxl-4 { + padding: 1.5rem !important; + } + + .p-xxl-5 { + padding: 3rem !important; + } + + .px-xxl-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-xxl-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-xxl-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-xxl-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-xxl-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-xxl-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-xxl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xxl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xxl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xxl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xxl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xxl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xxl-0 { + padding-top: 0 !important; + } + + .pt-xxl-1 { + padding-top: 0.25rem !important; + } + + .pt-xxl-2 { + padding-top: 0.5rem !important; + } + + .pt-xxl-3 { + padding-top: 1rem !important; + } + + .pt-xxl-4 { + padding-top: 1.5rem !important; + } + + .pt-xxl-5 { + padding-top: 3rem !important; + } + + .pe-xxl-0 { + padding-left: 0 !important; + } + + .pe-xxl-1 { + padding-left: 0.25rem !important; + } + + .pe-xxl-2 { + padding-left: 0.5rem !important; + } + + .pe-xxl-3 { + padding-left: 1rem !important; + } + + .pe-xxl-4 { + padding-left: 1.5rem !important; + } + + .pe-xxl-5 { + padding-left: 3rem !important; + } + + .pb-xxl-0 { + padding-bottom: 0 !important; + } + + .pb-xxl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xxl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xxl-3 { + padding-bottom: 1rem !important; + } + + .pb-xxl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xxl-5 { + padding-bottom: 3rem !important; + } + + .ps-xxl-0 { + padding-right: 0 !important; + } + + .ps-xxl-1 { + padding-right: 0.25rem !important; + } + + .ps-xxl-2 { + padding-right: 0.5rem !important; + } + + .ps-xxl-3 { + padding-right: 1rem !important; + } + + .ps-xxl-4 { + padding-right: 1.5rem !important; + } + + .ps-xxl-5 { + padding-right: 3rem !important; + } + + .gap-xxl-0 { + gap: 0 !important; + } + + .gap-xxl-1 { + gap: 0.25rem !important; + } + + .gap-xxl-2 { + gap: 0.5rem !important; + } + + .gap-xxl-3 { + gap: 1rem !important; + } + + .gap-xxl-4 { + gap: 1.5rem !important; + } + + .gap-xxl-5 { + gap: 3rem !important; + } + + .row-gap-xxl-0 { + row-gap: 0 !important; + } + + .row-gap-xxl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xxl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xxl-3 { + row-gap: 1rem !important; + } + + .row-gap-xxl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xxl-5 { + row-gap: 3rem !important; + } + + .column-gap-xxl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xxl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xxl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xxl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xxl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xxl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xxl-start { + text-align: right !important; + } + + .text-xxl-end { + text-align: left !important; + } + + .text-xxl-center { + text-align: center !important; + } } + @media (min-width: 1200px) { - .fs-1 { - font-size: 2.5rem !important; - } - .fs-2 { - font-size: 2rem !important; - } - .fs-3 { - font-size: 1.75rem !important; - } - .fs-4 { - font-size: 1.5rem !important; - } + .fs-1 { + font-size: 2.5rem !important; + } + + .fs-2 { + font-size: 2rem !important; + } + + .fs-3 { + font-size: 1.75rem !important; + } + + .fs-4 { + font-size: 1.5rem !important; + } } + @media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-grid { + display: grid !important; + } + + .d-print-inline-grid { + display: inline-grid !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: flex !important; + } + + .d-print-inline-flex { + display: inline-flex !important; + } + + .d-print-none { + display: none !important; + } } -/*# sourceMappingURL=bootstrap-utilities.rtl.css.map */ \ No newline at end of file + +/*# sourceMappingURL=bootstrap-utilities.rtl.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.css index b7ab57f2ad..2cfbb7feb9 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.css @@ -6,427 +6,435 @@ */ :root, [data-bs-theme=light] { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-black: #000; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #0d6efd; - --bs-secondary: #6c757d; - --bs-success: #198754; - --bs-info: #0dcaf0; - --bs-warning: #ffc107; - --bs-danger: #dc3545; - --bs-light: #f8f9fa; - --bs-dark: #212529; - --bs-primary-rgb: 13, 110, 253; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 25, 135, 84; - --bs-info-rgb: 13, 202, 240; - --bs-warning-rgb: 255, 193, 7; - --bs-danger-rgb: 220, 53, 69; - --bs-light-rgb: 248, 249, 250; - --bs-dark-rgb: 33, 37, 41; - --bs-primary-text-emphasis: #052c65; - --bs-secondary-text-emphasis: #2b2f32; - --bs-success-text-emphasis: #0a3622; - --bs-info-text-emphasis: #055160; - --bs-warning-text-emphasis: #664d03; - --bs-danger-text-emphasis: #58151c; - --bs-light-text-emphasis: #495057; - --bs-dark-text-emphasis: #495057; - --bs-primary-bg-subtle: #cfe2ff; - --bs-secondary-bg-subtle: #e2e3e5; - --bs-success-bg-subtle: #d1e7dd; - --bs-info-bg-subtle: #cff4fc; - --bs-warning-bg-subtle: #fff3cd; - --bs-danger-bg-subtle: #f8d7da; - --bs-light-bg-subtle: #fcfcfd; - --bs-dark-bg-subtle: #ced4da; - --bs-primary-border-subtle: #9ec5fe; - --bs-secondary-border-subtle: #c4c8cb; - --bs-success-border-subtle: #a3cfbb; - --bs-info-border-subtle: #9eeaf9; - --bs-warning-border-subtle: #ffe69c; - --bs-danger-border-subtle: #f1aeb5; - --bs-light-border-subtle: #e9ecef; - --bs-dark-border-subtle: #adb5bd; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-body-font-family: var(--bs-font-sans-serif); - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #212529; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg: #fff; - --bs-body-bg-rgb: 255, 255, 255; - --bs-emphasis-color: #000; - --bs-emphasis-color-rgb: 0, 0, 0; - --bs-secondary-color: rgba(33, 37, 41, 0.75); - --bs-secondary-color-rgb: 33, 37, 41; - --bs-secondary-bg: #e9ecef; - --bs-secondary-bg-rgb: 233, 236, 239; - --bs-tertiary-color: rgba(33, 37, 41, 0.5); - --bs-tertiary-color-rgb: 33, 37, 41; - --bs-tertiary-bg: #f8f9fa; - --bs-tertiary-bg-rgb: 248, 249, 250; - --bs-heading-color: inherit; - --bs-link-color: #0d6efd; - --bs-link-color-rgb: 13, 110, 253; - --bs-link-decoration: underline; - --bs-link-hover-color: #0a58ca; - --bs-link-hover-color-rgb: 10, 88, 202; - --bs-code-color: #d63384; - --bs-highlight-color: #212529; - --bs-highlight-bg: #fff3cd; - --bs-border-width: 1px; - --bs-border-style: solid; - --bs-border-color: #dee2e6; - --bs-border-color-translucent: rgba(0, 0, 0, 0.175); - --bs-border-radius: 0.375rem; - --bs-border-radius-sm: 0.25rem; - --bs-border-radius-lg: 0.5rem; - --bs-border-radius-xl: 1rem; - --bs-border-radius-xxl: 2rem; - --bs-border-radius-2xl: var(--bs-border-radius-xxl); - --bs-border-radius-pill: 50rem; - --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); - --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); - --bs-focus-ring-width: 0.25rem; - --bs-focus-ring-opacity: 0.25; - --bs-focus-ring-color: rgba(13, 110, 253, 0.25); - --bs-form-valid-color: #198754; - --bs-form-valid-border-color: #198754; - --bs-form-invalid-color: #dc3545; - --bs-form-invalid-border-color: #dc3545; + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-black: #000; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #0d6efd; + --bs-secondary: #6c757d; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-primary-rgb: 13, 110, 253; + --bs-secondary-rgb: 108, 117, 125; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-primary-text-emphasis: #052c65; + --bs-secondary-text-emphasis: #2b2f32; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #cfe2ff; + --bs-secondary-bg-subtle: #e2e3e5; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-primary-border-subtle: #9ec5fe; + --bs-secondary-border-subtle: #c4c8cb; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-body-font-family: var(--bs-font-sans-serif); + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #0d6efd; + --bs-link-color-rgb: 13, 110, 253; + --bs-link-decoration: underline; + --bs-link-hover-color: #0a58ca; + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-code-color: #d63384; + --bs-highlight-color: #212529; + --bs-highlight-bg: #fff3cd; + --bs-border-width: 1px; + --bs-border-style: solid; + --bs-border-color: #dee2e6; + --bs-border-color-translucent: rgba(0, 0, 0, 0.175); + --bs-border-radius: 0.375rem; + --bs-border-radius-sm: 0.25rem; + --bs-border-radius-lg: 0.5rem; + --bs-border-radius-xl: 1rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); + --bs-border-radius-pill: 50rem; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(13, 110, 253, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; } [data-bs-theme=dark] { - color-scheme: dark; - --bs-body-color: #dee2e6; - --bs-body-color-rgb: 222, 226, 230; - --bs-body-bg: #212529; - --bs-body-bg-rgb: 33, 37, 41; - --bs-emphasis-color: #fff; - --bs-emphasis-color-rgb: 255, 255, 255; - --bs-secondary-color: rgba(222, 226, 230, 0.75); - --bs-secondary-color-rgb: 222, 226, 230; - --bs-secondary-bg: #343a40; - --bs-secondary-bg-rgb: 52, 58, 64; - --bs-tertiary-color: rgba(222, 226, 230, 0.5); - --bs-tertiary-color-rgb: 222, 226, 230; - --bs-tertiary-bg: #2b3035; - --bs-tertiary-bg-rgb: 43, 48, 53; - --bs-primary-text-emphasis: #6ea8fe; - --bs-secondary-text-emphasis: #a7acb1; - --bs-success-text-emphasis: #75b798; - --bs-info-text-emphasis: #6edff6; - --bs-warning-text-emphasis: #ffda6a; - --bs-danger-text-emphasis: #ea868f; - --bs-light-text-emphasis: #f8f9fa; - --bs-dark-text-emphasis: #dee2e6; - --bs-primary-bg-subtle: #031633; - --bs-secondary-bg-subtle: #161719; - --bs-success-bg-subtle: #051b11; - --bs-info-bg-subtle: #032830; - --bs-warning-bg-subtle: #332701; - --bs-danger-bg-subtle: #2c0b0e; - --bs-light-bg-subtle: #343a40; - --bs-dark-bg-subtle: #1a1d20; - --bs-primary-border-subtle: #084298; - --bs-secondary-border-subtle: #41464b; - --bs-success-border-subtle: #0f5132; - --bs-info-border-subtle: #087990; - --bs-warning-border-subtle: #997404; - --bs-danger-border-subtle: #842029; - --bs-light-border-subtle: #495057; - --bs-dark-border-subtle: #343a40; - --bs-heading-color: inherit; - --bs-link-color: #6ea8fe; - --bs-link-hover-color: #8bb9fe; - --bs-link-color-rgb: 110, 168, 254; - --bs-link-hover-color-rgb: 139, 185, 254; - --bs-code-color: #e685b5; - --bs-highlight-color: #dee2e6; - --bs-highlight-bg: #664d03; - --bs-border-color: #495057; - --bs-border-color-translucent: rgba(255, 255, 255, 0.15); - --bs-form-valid-color: #75b798; - --bs-form-valid-border-color: #75b798; - --bs-form-invalid-color: #ea868f; - --bs-form-invalid-border-color: #ea868f; + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #6ea8fe; + --bs-secondary-text-emphasis: #a7acb1; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-primary-bg-subtle: #031633; + --bs-secondary-bg-subtle: #161719; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-primary-border-subtle: #084298; + --bs-secondary-border-subtle: #41464b; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-heading-color: inherit; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-code-color: #e685b5; + --bs-highlight-color: #dee2e6; + --bs-highlight-bg: #664d03; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; } *, *::before, *::after { - box-sizing: border-box; + box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { - :root { - scroll-behavior: smooth; - } + :root { + scroll-behavior: smooth; + } } body { - margin: 0; - font-family: var(--bs-body-font-family); - font-size: var(--bs-body-font-size); - font-weight: var(--bs-body-font-weight); - line-height: var(--bs-body-line-height); - color: var(--bs-body-color); - text-align: var(--bs-body-text-align); - background-color: var(--bs-body-bg); - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + margin: 0; + font-family: var(--bs-body-font-family); + font-size: var(--bs-body-font-size); + font-weight: var(--bs-body-font-weight); + line-height: var(--bs-body-line-height); + color: var(--bs-body-color); + text-align: var(--bs-body-text-align); + background-color: var(--bs-body-bg); + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { - margin: 1rem 0; - color: inherit; - border: 0; - border-top: var(--bs-border-width) solid; - opacity: 0.25; + margin: 1rem 0; + color: inherit; + border: 0; + border-top: var(--bs-border-width) solid; + opacity: 0.25; } h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { - margin-top: 0; - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; - color: var(--bs-heading-color); + margin-top: 0; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; + color: var(--bs-heading-color); } h1, .h1 { - font-size: calc(1.375rem + 1.5vw); + font-size: calc(1.375rem + 1.5vw); } + @media (min-width: 1200px) { - h1, .h1 { - font-size: 2.5rem; - } + h1, .h1 { + font-size: 2.5rem; + } } h2, .h2 { - font-size: calc(1.325rem + 0.9vw); + font-size: calc(1.325rem + 0.9vw); } + @media (min-width: 1200px) { - h2, .h2 { - font-size: 2rem; - } + h2, .h2 { + font-size: 2rem; + } } h3, .h3 { - font-size: calc(1.3rem + 0.6vw); + font-size: calc(1.3rem + 0.6vw); } + @media (min-width: 1200px) { - h3, .h3 { - font-size: 1.75rem; - } + h3, .h3 { + font-size: 1.75rem; + } } h4, .h4 { - font-size: calc(1.275rem + 0.3vw); + font-size: calc(1.275rem + 0.3vw); } + @media (min-width: 1200px) { - h4, .h4 { - font-size: 1.5rem; - } + h4, .h4 { + font-size: 1.5rem; + } } h5, .h5 { - font-size: 1.25rem; + font-size: 1.25rem; } h6, .h6 { - font-size: 1rem; + font-size: 1rem; } p { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } abbr[title] { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none; } address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; } ol, ul { - padding-left: 2rem; + padding-left: 2rem; } ol, ul, dl { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { - margin-bottom: 0; + margin-bottom: 0; } dt { - font-weight: 700; + font-weight: 700; } dd { - margin-bottom: 0.5rem; - margin-left: 0; + margin-bottom: 0.5rem; + margin-left: 0; } blockquote { - margin: 0 0 1rem; + margin: 0 0 1rem; } b, strong { - font-weight: bolder; + font-weight: bolder; } small, .small { - font-size: 0.875em; + font-size: 0.875em; } mark, .mark { - padding: 0.1875em; - color: var(--bs-highlight-color); - background-color: var(--bs-highlight-bg); + padding: 0.1875em; + color: var(--bs-highlight-color); + background-color: var(--bs-highlight-bg); } sub, sup { - position: relative; - font-size: 0.75em; - line-height: 0; - vertical-align: baseline; + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; } sub { - bottom: -0.25em; + bottom: -0.25em; } sup { - top: -0.5em; + top: -0.5em; } a { - color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); - text-decoration: underline; + color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); + text-decoration: underline; } + a:hover { - --bs-link-color-rgb: var(--bs-link-hover-color-rgb); + --bs-link-color-rgb: var(--bs-link-hover-color-rgb); } a:not([href]):not([class]), a:not([href]):not([class]):hover { - color: inherit; - text-decoration: none; + color: inherit; + text-decoration: none; } pre, code, kbd, samp { - font-family: var(--bs-font-monospace); - font-size: 1em; + font-family: var(--bs-font-monospace); + font-size: 1em; } pre { - display: block; - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - font-size: 0.875em; + display: block; + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + font-size: 0.875em; } + pre code { - font-size: inherit; - color: inherit; - word-break: normal; + font-size: inherit; + color: inherit; + word-break: normal; } code { - font-size: 0.875em; - color: var(--bs-code-color); - word-wrap: break-word; + font-size: 0.875em; + color: var(--bs-code-color); + word-wrap: break-word; } + a > code { - color: inherit; + color: inherit; } kbd { - padding: 0.1875rem 0.375rem; - font-size: 0.875em; - color: var(--bs-body-bg); - background-color: var(--bs-body-color); - border-radius: 0.25rem; + padding: 0.1875rem 0.375rem; + font-size: 0.875em; + color: var(--bs-body-bg); + background-color: var(--bs-body-color); + border-radius: 0.25rem; } + kbd kbd { - padding: 0; - font-size: 1em; + padding: 0; + font-size: 1em; } figure { - margin: 0 0 1rem; + margin: 0 0 1rem; } img, svg { - vertical-align: middle; + vertical-align: middle; } table { - caption-side: bottom; - border-collapse: collapse; + caption-side: bottom; + border-collapse: collapse; } caption { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: var(--bs-secondary-color); - text-align: left; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-secondary-color); + text-align: left; } th { - text-align: inherit; - text-align: -webkit-match-parent; + text-align: inherit; + text-align: -webkit-match-parent; } thead, @@ -435,21 +443,21 @@ tfoot, tr, td, th { - border-color: inherit; - border-style: solid; - border-width: 0; + border-color: inherit; + border-style: solid; + border-width: 0; } label { - display: inline-block; + display: inline-block; } button { - border-radius: 0; + border-radius: 0; } button:focus:not(:focus-visible) { - outline: 0; + outline: 0; } input, @@ -457,76 +465,80 @@ button, select, optgroup, textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; } button, select { - text-transform: none; + text-transform: none; } [role=button] { - cursor: pointer; + cursor: pointer; } select { - word-wrap: normal; + word-wrap: normal; } + select:disabled { - opacity: 1; + opacity: 1; } [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { - display: none !important; + display: none !important; } button, [type=button], [type=reset], [type=submit] { - -webkit-appearance: button; + -webkit-appearance: button; } + button:not(:disabled), [type=button]:not(:disabled), [type=reset]:not(:disabled), [type=submit]:not(:disabled) { - cursor: pointer; + cursor: pointer; } ::-moz-focus-inner { - padding: 0; - border-style: none; + padding: 0; + border-style: none; } textarea { - resize: vertical; + resize: vertical; } fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; + min-width: 0; + padding: 0; + margin: 0; + border: 0; } legend { - float: left; - width: 100%; - padding: 0; - margin-bottom: 0.5rem; - font-size: calc(1.275rem + 0.3vw); - line-height: inherit; + float: left; + width: 100%; + padding: 0; + margin-bottom: 0.5rem; + font-size: calc(1.275rem + 0.3vw); + line-height: inherit; } + @media (min-width: 1200px) { - legend { - font-size: 1.5rem; - } + legend { + font-size: 1.5rem; + } } + legend + * { - clear: left; + clear: left; } ::-webkit-datetime-edit-fields-wrapper, @@ -536,16 +548,16 @@ legend + * { ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { - padding: 0; + padding: 0; } ::-webkit-inner-spin-button { - height: auto; + height: auto; } [type=search] { - -webkit-appearance: textfield; - outline-offset: -2px; + -webkit-appearance: textfield; + outline-offset: -2px; } /* rtl:raw: @@ -557,181 +569,190 @@ legend + * { } */ ::-webkit-search-decoration { - -webkit-appearance: none; + -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { - padding: 0; + padding: 0; } ::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } ::file-selector-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } output { - display: inline-block; + display: inline-block; } iframe { - border: 0; + border: 0; } summary { - display: list-item; - cursor: pointer; + display: list-item; + cursor: pointer; } progress { - vertical-align: baseline; + vertical-align: baseline; } [hidden] { - display: none !important; + display: none !important; } .lead { - font-size: 1.25rem; - font-weight: 300; + font-size: 1.25rem; + font-weight: 300; } .display-1 { - font-size: calc(1.625rem + 4.5vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.625rem + 4.5vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-1 { - font-size: 5rem; - } + .display-1 { + font-size: 5rem; + } } .display-2 { - font-size: calc(1.575rem + 3.9vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.575rem + 3.9vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-2 { - font-size: 4.5rem; - } + .display-2 { + font-size: 4.5rem; + } } .display-3 { - font-size: calc(1.525rem + 3.3vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.525rem + 3.3vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-3 { - font-size: 4rem; - } + .display-3 { + font-size: 4rem; + } } .display-4 { - font-size: calc(1.475rem + 2.7vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.475rem + 2.7vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-4 { - font-size: 3.5rem; - } + .display-4 { + font-size: 3.5rem; + } } .display-5 { - font-size: calc(1.425rem + 2.1vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.425rem + 2.1vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-5 { - font-size: 3rem; - } + .display-5 { + font-size: 3rem; + } } .display-6 { - font-size: calc(1.375rem + 1.5vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.375rem + 1.5vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-6 { - font-size: 2.5rem; - } + .display-6 { + font-size: 2.5rem; + } } .list-unstyled { - padding-left: 0; - list-style: none; + padding-left: 0; + list-style: none; } .list-inline { - padding-left: 0; - list-style: none; + padding-left: 0; + list-style: none; } .list-inline-item { - display: inline-block; + display: inline-block; } + .list-inline-item:not(:last-child) { - margin-right: 0.5rem; + margin-right: 0.5rem; } .initialism { - font-size: 0.875em; - text-transform: uppercase; + font-size: 0.875em; + text-transform: uppercase; } .blockquote { - margin-bottom: 1rem; - font-size: 1.25rem; + margin-bottom: 1rem; + font-size: 1.25rem; } + .blockquote > :last-child { - margin-bottom: 0; + margin-bottom: 0; } .blockquote-footer { - margin-top: -1rem; - margin-bottom: 1rem; - font-size: 0.875em; - color: #6c757d; + margin-top: -1rem; + margin-bottom: 1rem; + font-size: 0.875em; + color: #6c757d; } + .blockquote-footer::before { - content: "— "; + content: "— "; } .img-fluid { - max-width: 100%; - height: auto; + max-width: 100%; + height: auto; } .img-thumbnail { - padding: 0.25rem; - background-color: var(--bs-body-bg); - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); - max-width: 100%; - height: auto; + padding: 0.25rem; + background-color: var(--bs-body-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + max-width: 100%; + height: auto; } .figure { - display: inline-block; + display: inline-block; } .figure-img { - margin-bottom: 0.5rem; - line-height: 1; + margin-bottom: 0.5rem; + line-height: 1; } .figure-caption { - font-size: 0.875em; - color: var(--bs-secondary-color); + font-size: 0.875em; + color: var(--bs-secondary-color); } .container, @@ -741,2137 +762,2476 @@ progress { .container-lg, .container-md, .container-sm { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - width: 100%; - padding-right: calc(var(--bs-gutter-x) * 0.5); - padding-left: calc(var(--bs-gutter-x) * 0.5); - margin-right: auto; - margin-left: auto; + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + width: 100%; + padding-right: calc(var(--bs-gutter-x) * 0.5); + padding-left: calc(var(--bs-gutter-x) * 0.5); + margin-right: auto; + margin-left: auto; } @media (min-width: 576px) { - .container-sm, .container { - max-width: 540px; - } + .container-sm, .container { + max-width: 540px; + } } + @media (min-width: 768px) { - .container-md, .container-sm, .container { - max-width: 720px; - } + .container-md, .container-sm, .container { + max-width: 720px; + } } + @media (min-width: 992px) { - .container-lg, .container-md, .container-sm, .container { - max-width: 960px; - } + .container-lg, .container-md, .container-sm, .container { + max-width: 960px; + } } + @media (min-width: 1200px) { - .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1140px; - } + .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1140px; + } } + @media (min-width: 1400px) { - .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1320px; - } + .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1320px; + } } + :root { - --bs-breakpoint-xs: 0; - --bs-breakpoint-sm: 576px; - --bs-breakpoint-md: 768px; - --bs-breakpoint-lg: 992px; - --bs-breakpoint-xl: 1200px; - --bs-breakpoint-xxl: 1400px; + --bs-breakpoint-xs: 0; + --bs-breakpoint-sm: 576px; + --bs-breakpoint-md: 768px; + --bs-breakpoint-lg: 992px; + --bs-breakpoint-xl: 1200px; + --bs-breakpoint-xxl: 1400px; } .row { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - display: flex; - flex-wrap: wrap; - margin-top: calc(-1 * var(--bs-gutter-y)); - margin-right: calc(-0.5 * var(--bs-gutter-x)); - margin-left: calc(-0.5 * var(--bs-gutter-x)); + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + display: flex; + flex-wrap: wrap; + margin-top: calc(-1 * var(--bs-gutter-y)); + margin-right: calc(-0.5 * var(--bs-gutter-x)); + margin-left: calc(-0.5 * var(--bs-gutter-x)); } + .row > * { - flex-shrink: 0; - width: 100%; - max-width: 100%; - padding-right: calc(var(--bs-gutter-x) * 0.5); - padding-left: calc(var(--bs-gutter-x) * 0.5); - margin-top: var(--bs-gutter-y); + flex-shrink: 0; + width: 100%; + max-width: 100%; + padding-right: calc(var(--bs-gutter-x) * 0.5); + padding-left: calc(var(--bs-gutter-x) * 0.5); + margin-top: var(--bs-gutter-y); } .col { - flex: 1 0 0%; + flex: 1 0 0%; } .row-cols-auto > * { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .row-cols-1 > * { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .row-cols-2 > * { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .row-cols-3 > * { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .row-cols-4 > * { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .row-cols-5 > * { - flex: 0 0 auto; - width: 20%; + flex: 0 0 auto; + width: 20%; } .row-cols-6 > * { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-auto { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .col-1 { - flex: 0 0 auto; - width: 8.33333333%; + flex: 0 0 auto; + width: 8.33333333%; } .col-2 { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-3 { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .col-4 { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .col-5 { - flex: 0 0 auto; - width: 41.66666667%; + flex: 0 0 auto; + width: 41.66666667%; } .col-6 { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .col-7 { - flex: 0 0 auto; - width: 58.33333333%; + flex: 0 0 auto; + width: 58.33333333%; } .col-8 { - flex: 0 0 auto; - width: 66.66666667%; + flex: 0 0 auto; + width: 66.66666667%; } .col-9 { - flex: 0 0 auto; - width: 75%; + flex: 0 0 auto; + width: 75%; } .col-10 { - flex: 0 0 auto; - width: 83.33333333%; + flex: 0 0 auto; + width: 83.33333333%; } .col-11 { - flex: 0 0 auto; - width: 91.66666667%; + flex: 0 0 auto; + width: 91.66666667%; } .col-12 { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .offset-1 { - margin-left: 8.33333333%; + margin-left: 8.33333333%; } .offset-2 { - margin-left: 16.66666667%; + margin-left: 16.66666667%; } .offset-3 { - margin-left: 25%; + margin-left: 25%; } .offset-4 { - margin-left: 33.33333333%; + margin-left: 33.33333333%; } .offset-5 { - margin-left: 41.66666667%; + margin-left: 41.66666667%; } .offset-6 { - margin-left: 50%; + margin-left: 50%; } .offset-7 { - margin-left: 58.33333333%; + margin-left: 58.33333333%; } .offset-8 { - margin-left: 66.66666667%; + margin-left: 66.66666667%; } .offset-9 { - margin-left: 75%; + margin-left: 75%; } .offset-10 { - margin-left: 83.33333333%; + margin-left: 83.33333333%; } .offset-11 { - margin-left: 91.66666667%; + margin-left: 91.66666667%; } .g-0, .gx-0 { - --bs-gutter-x: 0; + --bs-gutter-x: 0; } .g-0, .gy-0 { - --bs-gutter-y: 0; + --bs-gutter-y: 0; } .g-1, .gx-1 { - --bs-gutter-x: 0.25rem; + --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { - --bs-gutter-y: 0.25rem; + --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { - --bs-gutter-x: 0.5rem; + --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { - --bs-gutter-y: 0.5rem; + --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { - --bs-gutter-x: 1rem; + --bs-gutter-x: 1rem; } .g-3, .gy-3 { - --bs-gutter-y: 1rem; + --bs-gutter-y: 1rem; } .g-4, .gx-4 { - --bs-gutter-x: 1.5rem; + --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { - --bs-gutter-y: 1.5rem; + --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { - --bs-gutter-x: 3rem; + --bs-gutter-x: 3rem; } .g-5, .gy-5 { - --bs-gutter-y: 3rem; + --bs-gutter-y: 3rem; } @media (min-width: 576px) { - .col-sm { - flex: 1 0 0%; - } - .row-cols-sm-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-sm-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-sm-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-sm-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-sm-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-sm-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-sm-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-auto { - flex: 0 0 auto; - width: auto; - } - .col-sm-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-sm-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-3 { - flex: 0 0 auto; - width: 25%; - } - .col-sm-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-sm-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-sm-6 { - flex: 0 0 auto; - width: 50%; - } - .col-sm-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-sm-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-sm-9 { - flex: 0 0 auto; - width: 75%; - } - .col-sm-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-sm-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-sm-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-sm-0 { - margin-left: 0; - } - .offset-sm-1 { - margin-left: 8.33333333%; - } - .offset-sm-2 { - margin-left: 16.66666667%; - } - .offset-sm-3 { - margin-left: 25%; - } - .offset-sm-4 { - margin-left: 33.33333333%; - } - .offset-sm-5 { - margin-left: 41.66666667%; - } - .offset-sm-6 { - margin-left: 50%; - } - .offset-sm-7 { - margin-left: 58.33333333%; - } - .offset-sm-8 { - margin-left: 66.66666667%; - } - .offset-sm-9 { - margin-left: 75%; - } - .offset-sm-10 { - margin-left: 83.33333333%; - } - .offset-sm-11 { - margin-left: 91.66666667%; - } - .g-sm-0, - .gx-sm-0 { - --bs-gutter-x: 0; - } - .g-sm-0, - .gy-sm-0 { - --bs-gutter-y: 0; - } - .g-sm-1, - .gx-sm-1 { - --bs-gutter-x: 0.25rem; - } - .g-sm-1, - .gy-sm-1 { - --bs-gutter-y: 0.25rem; - } - .g-sm-2, - .gx-sm-2 { - --bs-gutter-x: 0.5rem; - } - .g-sm-2, - .gy-sm-2 { - --bs-gutter-y: 0.5rem; - } - .g-sm-3, - .gx-sm-3 { - --bs-gutter-x: 1rem; - } - .g-sm-3, - .gy-sm-3 { - --bs-gutter-y: 1rem; - } - .g-sm-4, - .gx-sm-4 { - --bs-gutter-x: 1.5rem; - } - .g-sm-4, - .gy-sm-4 { - --bs-gutter-y: 1.5rem; - } - .g-sm-5, - .gx-sm-5 { - --bs-gutter-x: 3rem; - } - .g-sm-5, - .gy-sm-5 { - --bs-gutter-y: 3rem; - } + .col-sm { + flex: 1 0 0%; + } + + .row-cols-sm-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-sm-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-sm-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-sm-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-sm-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-sm-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-sm-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + } + + .col-sm-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-sm-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-sm-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-sm-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-sm-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-sm-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-sm-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-sm-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-sm-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-sm-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-sm-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-sm-0 { + margin-left: 0; + } + + .offset-sm-1 { + margin-left: 8.33333333%; + } + + .offset-sm-2 { + margin-left: 16.66666667%; + } + + .offset-sm-3 { + margin-left: 25%; + } + + .offset-sm-4 { + margin-left: 33.33333333%; + } + + .offset-sm-5 { + margin-left: 41.66666667%; + } + + .offset-sm-6 { + margin-left: 50%; + } + + .offset-sm-7 { + margin-left: 58.33333333%; + } + + .offset-sm-8 { + margin-left: 66.66666667%; + } + + .offset-sm-9 { + margin-left: 75%; + } + + .offset-sm-10 { + margin-left: 83.33333333%; + } + + .offset-sm-11 { + margin-left: 91.66666667%; + } + + .g-sm-0, + .gx-sm-0 { + --bs-gutter-x: 0; + } + + .g-sm-0, + .gy-sm-0 { + --bs-gutter-y: 0; + } + + .g-sm-1, + .gx-sm-1 { + --bs-gutter-x: 0.25rem; + } + + .g-sm-1, + .gy-sm-1 { + --bs-gutter-y: 0.25rem; + } + + .g-sm-2, + .gx-sm-2 { + --bs-gutter-x: 0.5rem; + } + + .g-sm-2, + .gy-sm-2 { + --bs-gutter-y: 0.5rem; + } + + .g-sm-3, + .gx-sm-3 { + --bs-gutter-x: 1rem; + } + + .g-sm-3, + .gy-sm-3 { + --bs-gutter-y: 1rem; + } + + .g-sm-4, + .gx-sm-4 { + --bs-gutter-x: 1.5rem; + } + + .g-sm-4, + .gy-sm-4 { + --bs-gutter-y: 1.5rem; + } + + .g-sm-5, + .gx-sm-5 { + --bs-gutter-x: 3rem; + } + + .g-sm-5, + .gy-sm-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 768px) { - .col-md { - flex: 1 0 0%; - } - .row-cols-md-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-md-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-md-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-md-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-md-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-md-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-md-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-auto { - flex: 0 0 auto; - width: auto; - } - .col-md-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-md-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-3 { - flex: 0 0 auto; - width: 25%; - } - .col-md-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-md-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-md-6 { - flex: 0 0 auto; - width: 50%; - } - .col-md-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-md-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-md-9 { - flex: 0 0 auto; - width: 75%; - } - .col-md-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-md-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-md-12 { - flex: 0 0 auto; + .col-md { + flex: 1 0 0%; + } + + .row-cols-md-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-md-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-md-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-md-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-md-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-md-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-md-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + } + + .col-md-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-md-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-md-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-md-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-md-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-md-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-md-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-md-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-md-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-md-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-md-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-md-0 { + margin-left: 0; + } + + .offset-md-1 { + margin-left: 8.33333333%; + } + + .offset-md-2 { + margin-left: 16.66666667%; + } + + .offset-md-3 { + margin-left: 25%; + } + + .offset-md-4 { + margin-left: 33.33333333%; + } + + .offset-md-5 { + margin-left: 41.66666667%; + } + + .offset-md-6 { + margin-left: 50%; + } + + .offset-md-7 { + margin-left: 58.33333333%; + } + + .offset-md-8 { + margin-left: 66.66666667%; + } + + .offset-md-9 { + margin-left: 75%; + } + + .offset-md-10 { + margin-left: 83.33333333%; + } + + .offset-md-11 { + margin-left: 91.66666667%; + } + + .g-md-0, + .gx-md-0 { + --bs-gutter-x: 0; + } + + .g-md-0, + .gy-md-0 { + --bs-gutter-y: 0; + } + + .g-md-1, + .gx-md-1 { + --bs-gutter-x: 0.25rem; + } + + .g-md-1, + .gy-md-1 { + --bs-gutter-y: 0.25rem; + } + + .g-md-2, + .gx-md-2 { + --bs-gutter-x: 0.5rem; + } + + .g-md-2, + .gy-md-2 { + --bs-gutter-y: 0.5rem; + } + + .g-md-3, + .gx-md-3 { + --bs-gutter-x: 1rem; + } + + .g-md-3, + .gy-md-3 { + --bs-gutter-y: 1rem; + } + + .g-md-4, + .gx-md-4 { + --bs-gutter-x: 1.5rem; + } + + .g-md-4, + .gy-md-4 { + --bs-gutter-y: 1.5rem; + } + + .g-md-5, + .gx-md-5 { + --bs-gutter-x: 3rem; + } + + .g-md-5, + .gy-md-5 { + --bs-gutter-y: 3rem; + } +} + +@media (min-width: 992px) { + .col-lg { + flex: 1 0 0%; + } + + .row-cols-lg-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-lg-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-lg-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-lg-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-lg-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-lg-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-lg-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + } + + .col-lg-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-lg-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-lg-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-lg-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-lg-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-lg-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-lg-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-lg-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-lg-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-lg-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-lg-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-lg-0 { + margin-left: 0; + } + + .offset-lg-1 { + margin-left: 8.33333333%; + } + + .offset-lg-2 { + margin-left: 16.66666667%; + } + + .offset-lg-3 { + margin-left: 25%; + } + + .offset-lg-4 { + margin-left: 33.33333333%; + } + + .offset-lg-5 { + margin-left: 41.66666667%; + } + + .offset-lg-6 { + margin-left: 50%; + } + + .offset-lg-7 { + margin-left: 58.33333333%; + } + + .offset-lg-8 { + margin-left: 66.66666667%; + } + + .offset-lg-9 { + margin-left: 75%; + } + + .offset-lg-10 { + margin-left: 83.33333333%; + } + + .offset-lg-11 { + margin-left: 91.66666667%; + } + + .g-lg-0, + .gx-lg-0 { + --bs-gutter-x: 0; + } + + .g-lg-0, + .gy-lg-0 { + --bs-gutter-y: 0; + } + + .g-lg-1, + .gx-lg-1 { + --bs-gutter-x: 0.25rem; + } + + .g-lg-1, + .gy-lg-1 { + --bs-gutter-y: 0.25rem; + } + + .g-lg-2, + .gx-lg-2 { + --bs-gutter-x: 0.5rem; + } + + .g-lg-2, + .gy-lg-2 { + --bs-gutter-y: 0.5rem; + } + + .g-lg-3, + .gx-lg-3 { + --bs-gutter-x: 1rem; + } + + .g-lg-3, + .gy-lg-3 { + --bs-gutter-y: 1rem; + } + + .g-lg-4, + .gx-lg-4 { + --bs-gutter-x: 1.5rem; + } + + .g-lg-4, + .gy-lg-4 { + --bs-gutter-y: 1.5rem; + } + + .g-lg-5, + .gx-lg-5 { + --bs-gutter-x: 3rem; + } + + .g-lg-5, + .gy-lg-5 { + --bs-gutter-y: 3rem; + } +} + +@media (min-width: 1200px) { + .col-xl { + flex: 1 0 0%; + } + + .row-cols-xl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xl-0 { + margin-left: 0; + } + + .offset-xl-1 { + margin-left: 8.33333333%; + } + + .offset-xl-2 { + margin-left: 16.66666667%; + } + + .offset-xl-3 { + margin-left: 25%; + } + + .offset-xl-4 { + margin-left: 33.33333333%; + } + + .offset-xl-5 { + margin-left: 41.66666667%; + } + + .offset-xl-6 { + margin-left: 50%; + } + + .offset-xl-7 { + margin-left: 58.33333333%; + } + + .offset-xl-8 { + margin-left: 66.66666667%; + } + + .offset-xl-9 { + margin-left: 75%; + } + + .offset-xl-10 { + margin-left: 83.33333333%; + } + + .offset-xl-11 { + margin-left: 91.66666667%; + } + + .g-xl-0, + .gx-xl-0 { + --bs-gutter-x: 0; + } + + .g-xl-0, + .gy-xl-0 { + --bs-gutter-y: 0; + } + + .g-xl-1, + .gx-xl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xl-1, + .gy-xl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xl-2, + .gx-xl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xl-2, + .gy-xl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xl-3, + .gx-xl-3 { + --bs-gutter-x: 1rem; + } + + .g-xl-3, + .gy-xl-3 { + --bs-gutter-y: 1rem; + } + + .g-xl-4, + .gx-xl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xl-4, + .gy-xl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xl-5, + .gx-xl-5 { + --bs-gutter-x: 3rem; + } + + .g-xl-5, + .gy-xl-5 { + --bs-gutter-y: 3rem; + } +} + +@media (min-width: 1400px) { + .col-xxl { + flex: 1 0 0%; + } + + .row-cols-xxl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xxl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xxl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xxl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xxl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xxl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xxl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xxl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xxl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xxl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xxl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xxl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xxl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xxl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xxl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xxl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xxl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xxl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xxl-0 { + margin-left: 0; + } + + .offset-xxl-1 { + margin-left: 8.33333333%; + } + + .offset-xxl-2 { + margin-left: 16.66666667%; + } + + .offset-xxl-3 { + margin-left: 25%; + } + + .offset-xxl-4 { + margin-left: 33.33333333%; + } + + .offset-xxl-5 { + margin-left: 41.66666667%; + } + + .offset-xxl-6 { + margin-left: 50%; + } + + .offset-xxl-7 { + margin-left: 58.33333333%; + } + + .offset-xxl-8 { + margin-left: 66.66666667%; + } + + .offset-xxl-9 { + margin-left: 75%; + } + + .offset-xxl-10 { + margin-left: 83.33333333%; + } + + .offset-xxl-11 { + margin-left: 91.66666667%; + } + + .g-xxl-0, + .gx-xxl-0 { + --bs-gutter-x: 0; + } + + .g-xxl-0, + .gy-xxl-0 { + --bs-gutter-y: 0; + } + + .g-xxl-1, + .gx-xxl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xxl-1, + .gy-xxl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xxl-2, + .gx-xxl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xxl-2, + .gy-xxl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xxl-3, + .gx-xxl-3 { + --bs-gutter-x: 1rem; + } + + .g-xxl-3, + .gy-xxl-3 { + --bs-gutter-y: 1rem; + } + + .g-xxl-4, + .gx-xxl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xxl-4, + .gy-xxl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xxl-5, + .gx-xxl-5 { + --bs-gutter-x: 3rem; + } + + .g-xxl-5, + .gy-xxl-5 { + --bs-gutter-y: 3rem; + } +} + +.table { + --bs-table-color-type: initial; + --bs-table-bg-type: initial; + --bs-table-color-state: initial; + --bs-table-bg-state: initial; + --bs-table-color: var(--bs-emphasis-color); + --bs-table-bg: var(--bs-body-bg); + --bs-table-border-color: var(--bs-border-color); + --bs-table-accent-bg: transparent; + --bs-table-striped-color: var(--bs-emphasis-color); + --bs-table-striped-bg: rgba(var(--bs-emphasis-color-rgb), 0.05); + --bs-table-active-color: var(--bs-emphasis-color); + --bs-table-active-bg: rgba(var(--bs-emphasis-color-rgb), 0.1); + --bs-table-hover-color: var(--bs-emphasis-color); + --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); width: 100%; - } - .offset-md-0 { - margin-left: 0; - } - .offset-md-1 { - margin-left: 8.33333333%; - } - .offset-md-2 { - margin-left: 16.66666667%; - } - .offset-md-3 { - margin-left: 25%; - } - .offset-md-4 { - margin-left: 33.33333333%; - } - .offset-md-5 { - margin-left: 41.66666667%; - } - .offset-md-6 { - margin-left: 50%; - } - .offset-md-7 { - margin-left: 58.33333333%; - } - .offset-md-8 { - margin-left: 66.66666667%; - } - .offset-md-9 { - margin-left: 75%; - } - .offset-md-10 { - margin-left: 83.33333333%; - } - .offset-md-11 { - margin-left: 91.66666667%; - } - .g-md-0, - .gx-md-0 { - --bs-gutter-x: 0; - } - .g-md-0, - .gy-md-0 { - --bs-gutter-y: 0; - } - .g-md-1, - .gx-md-1 { - --bs-gutter-x: 0.25rem; - } - .g-md-1, - .gy-md-1 { - --bs-gutter-y: 0.25rem; - } - .g-md-2, - .gx-md-2 { - --bs-gutter-x: 0.5rem; - } - .g-md-2, - .gy-md-2 { - --bs-gutter-y: 0.5rem; - } - .g-md-3, - .gx-md-3 { - --bs-gutter-x: 1rem; - } - .g-md-3, - .gy-md-3 { - --bs-gutter-y: 1rem; - } - .g-md-4, - .gx-md-4 { - --bs-gutter-x: 1.5rem; - } - .g-md-4, - .gy-md-4 { - --bs-gutter-y: 1.5rem; - } - .g-md-5, - .gx-md-5 { - --bs-gutter-x: 3rem; - } - .g-md-5, - .gy-md-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 992px) { - .col-lg { - flex: 1 0 0%; - } - .row-cols-lg-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-lg-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-lg-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-lg-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-lg-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-lg-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-lg-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-auto { - flex: 0 0 auto; - width: auto; - } - .col-lg-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-lg-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-3 { - flex: 0 0 auto; - width: 25%; - } - .col-lg-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-lg-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-lg-6 { - flex: 0 0 auto; - width: 50%; - } - .col-lg-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-lg-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-lg-9 { - flex: 0 0 auto; - width: 75%; - } - .col-lg-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-lg-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-lg-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-lg-0 { - margin-left: 0; - } - .offset-lg-1 { - margin-left: 8.33333333%; - } - .offset-lg-2 { - margin-left: 16.66666667%; - } - .offset-lg-3 { - margin-left: 25%; - } - .offset-lg-4 { - margin-left: 33.33333333%; - } - .offset-lg-5 { - margin-left: 41.66666667%; - } - .offset-lg-6 { - margin-left: 50%; - } - .offset-lg-7 { - margin-left: 58.33333333%; - } - .offset-lg-8 { - margin-left: 66.66666667%; - } - .offset-lg-9 { - margin-left: 75%; - } - .offset-lg-10 { - margin-left: 83.33333333%; - } - .offset-lg-11 { - margin-left: 91.66666667%; - } - .g-lg-0, - .gx-lg-0 { - --bs-gutter-x: 0; - } - .g-lg-0, - .gy-lg-0 { - --bs-gutter-y: 0; - } - .g-lg-1, - .gx-lg-1 { - --bs-gutter-x: 0.25rem; - } - .g-lg-1, - .gy-lg-1 { - --bs-gutter-y: 0.25rem; - } - .g-lg-2, - .gx-lg-2 { - --bs-gutter-x: 0.5rem; - } - .g-lg-2, - .gy-lg-2 { - --bs-gutter-y: 0.5rem; - } - .g-lg-3, - .gx-lg-3 { - --bs-gutter-x: 1rem; - } - .g-lg-3, - .gy-lg-3 { - --bs-gutter-y: 1rem; - } - .g-lg-4, - .gx-lg-4 { - --bs-gutter-x: 1.5rem; - } - .g-lg-4, - .gy-lg-4 { - --bs-gutter-y: 1.5rem; - } - .g-lg-5, - .gx-lg-5 { - --bs-gutter-x: 3rem; - } - .g-lg-5, - .gy-lg-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1200px) { - .col-xl { - flex: 1 0 0%; - } - .row-cols-xl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xl-0 { - margin-left: 0; - } - .offset-xl-1 { - margin-left: 8.33333333%; - } - .offset-xl-2 { - margin-left: 16.66666667%; - } - .offset-xl-3 { - margin-left: 25%; - } - .offset-xl-4 { - margin-left: 33.33333333%; - } - .offset-xl-5 { - margin-left: 41.66666667%; - } - .offset-xl-6 { - margin-left: 50%; - } - .offset-xl-7 { - margin-left: 58.33333333%; - } - .offset-xl-8 { - margin-left: 66.66666667%; - } - .offset-xl-9 { - margin-left: 75%; - } - .offset-xl-10 { - margin-left: 83.33333333%; - } - .offset-xl-11 { - margin-left: 91.66666667%; - } - .g-xl-0, - .gx-xl-0 { - --bs-gutter-x: 0; - } - .g-xl-0, - .gy-xl-0 { - --bs-gutter-y: 0; - } - .g-xl-1, - .gx-xl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xl-1, - .gy-xl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xl-2, - .gx-xl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xl-2, - .gy-xl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xl-3, - .gx-xl-3 { - --bs-gutter-x: 1rem; - } - .g-xl-3, - .gy-xl-3 { - --bs-gutter-y: 1rem; - } - .g-xl-4, - .gx-xl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xl-4, - .gy-xl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xl-5, - .gx-xl-5 { - --bs-gutter-x: 3rem; - } - .g-xl-5, - .gy-xl-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1400px) { - .col-xxl { - flex: 1 0 0%; - } - .row-cols-xxl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xxl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xxl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xxl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xxl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xxl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xxl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xxl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xxl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xxl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xxl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xxl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xxl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xxl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xxl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xxl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xxl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xxl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xxl-0 { - margin-left: 0; - } - .offset-xxl-1 { - margin-left: 8.33333333%; - } - .offset-xxl-2 { - margin-left: 16.66666667%; - } - .offset-xxl-3 { - margin-left: 25%; - } - .offset-xxl-4 { - margin-left: 33.33333333%; - } - .offset-xxl-5 { - margin-left: 41.66666667%; - } - .offset-xxl-6 { - margin-left: 50%; - } - .offset-xxl-7 { - margin-left: 58.33333333%; - } - .offset-xxl-8 { - margin-left: 66.66666667%; - } - .offset-xxl-9 { - margin-left: 75%; - } - .offset-xxl-10 { - margin-left: 83.33333333%; - } - .offset-xxl-11 { - margin-left: 91.66666667%; - } - .g-xxl-0, - .gx-xxl-0 { - --bs-gutter-x: 0; - } - .g-xxl-0, - .gy-xxl-0 { - --bs-gutter-y: 0; - } - .g-xxl-1, - .gx-xxl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xxl-1, - .gy-xxl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xxl-2, - .gx-xxl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xxl-2, - .gy-xxl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xxl-3, - .gx-xxl-3 { - --bs-gutter-x: 1rem; - } - .g-xxl-3, - .gy-xxl-3 { - --bs-gutter-y: 1rem; - } - .g-xxl-4, - .gx-xxl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xxl-4, - .gy-xxl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xxl-5, - .gx-xxl-5 { - --bs-gutter-x: 3rem; - } - .g-xxl-5, - .gy-xxl-5 { - --bs-gutter-y: 3rem; - } -} -.table { - --bs-table-color-type: initial; - --bs-table-bg-type: initial; - --bs-table-color-state: initial; - --bs-table-bg-state: initial; - --bs-table-color: var(--bs-emphasis-color); - --bs-table-bg: var(--bs-body-bg); - --bs-table-border-color: var(--bs-border-color); - --bs-table-accent-bg: transparent; - --bs-table-striped-color: var(--bs-emphasis-color); - --bs-table-striped-bg: rgba(var(--bs-emphasis-color-rgb), 0.05); - --bs-table-active-color: var(--bs-emphasis-color); - --bs-table-active-bg: rgba(var(--bs-emphasis-color-rgb), 0.1); - --bs-table-hover-color: var(--bs-emphasis-color); - --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); - width: 100%; - margin-bottom: 1rem; - vertical-align: top; - border-color: var(--bs-table-border-color); + margin-bottom: 1rem; + vertical-align: top; + border-color: var(--bs-table-border-color); } + .table > :not(caption) > * > * { - padding: 0.5rem 0.5rem; - color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color))); - background-color: var(--bs-table-bg); - border-bottom-width: var(--bs-border-width); - box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); + padding: 0.5rem 0.5rem; + color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color))); + background-color: var(--bs-table-bg); + border-bottom-width: var(--bs-border-width); + box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); } + .table > tbody { - vertical-align: inherit; + vertical-align: inherit; } + .table > thead { - vertical-align: bottom; + vertical-align: bottom; } .table-group-divider { - border-top: calc(var(--bs-border-width) * 2) solid currentcolor; + border-top: calc(var(--bs-border-width) * 2) solid currentcolor; } .caption-top { - caption-side: top; + caption-side: top; } .table-sm > :not(caption) > * > * { - padding: 0.25rem 0.25rem; + padding: 0.25rem 0.25rem; } .table-bordered > :not(caption) > * { - border-width: var(--bs-border-width) 0; + border-width: var(--bs-border-width) 0; } + .table-bordered > :not(caption) > * > * { - border-width: 0 var(--bs-border-width); + border-width: 0 var(--bs-border-width); } .table-borderless > :not(caption) > * > * { - border-bottom-width: 0; + border-bottom-width: 0; } + .table-borderless > :not(:first-child) { - border-top-width: 0; + border-top-width: 0; } .table-striped > tbody > tr:nth-of-type(odd) > * { - --bs-table-color-type: var(--bs-table-striped-color); - --bs-table-bg-type: var(--bs-table-striped-bg); + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); } .table-striped-columns > :not(caption) > tr > :nth-child(even) { - --bs-table-color-type: var(--bs-table-striped-color); - --bs-table-bg-type: var(--bs-table-striped-bg); + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); } .table-active { - --bs-table-color-state: var(--bs-table-active-color); - --bs-table-bg-state: var(--bs-table-active-bg); + --bs-table-color-state: var(--bs-table-active-color); + --bs-table-bg-state: var(--bs-table-active-bg); } .table-hover > tbody > tr:hover > * { - --bs-table-color-state: var(--bs-table-hover-color); - --bs-table-bg-state: var(--bs-table-hover-bg); + --bs-table-color-state: var(--bs-table-hover-color); + --bs-table-bg-state: var(--bs-table-hover-bg); } .table-primary { - --bs-table-color: #000; - --bs-table-bg: #cfe2ff; - --bs-table-border-color: #a6b5cc; - --bs-table-striped-bg: #c5d7f2; - --bs-table-striped-color: #000; - --bs-table-active-bg: #bacbe6; - --bs-table-active-color: #000; - --bs-table-hover-bg: #bfd1ec; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #cfe2ff; + --bs-table-border-color: #a6b5cc; + --bs-table-striped-bg: #c5d7f2; + --bs-table-striped-color: #000; + --bs-table-active-bg: #bacbe6; + --bs-table-active-color: #000; + --bs-table-hover-bg: #bfd1ec; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-secondary { - --bs-table-color: #000; - --bs-table-bg: #e2e3e5; - --bs-table-border-color: #b5b6b7; - --bs-table-striped-bg: #d7d8da; - --bs-table-striped-color: #000; - --bs-table-active-bg: #cbccce; - --bs-table-active-color: #000; - --bs-table-hover-bg: #d1d2d4; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #e2e3e5; + --bs-table-border-color: #b5b6b7; + --bs-table-striped-bg: #d7d8da; + --bs-table-striped-color: #000; + --bs-table-active-bg: #cbccce; + --bs-table-active-color: #000; + --bs-table-hover-bg: #d1d2d4; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-success { - --bs-table-color: #000; - --bs-table-bg: #d1e7dd; - --bs-table-border-color: #a7b9b1; - --bs-table-striped-bg: #c7dbd2; - --bs-table-striped-color: #000; - --bs-table-active-bg: #bcd0c7; - --bs-table-active-color: #000; - --bs-table-hover-bg: #c1d6cc; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #d1e7dd; + --bs-table-border-color: #a7b9b1; + --bs-table-striped-bg: #c7dbd2; + --bs-table-striped-color: #000; + --bs-table-active-bg: #bcd0c7; + --bs-table-active-color: #000; + --bs-table-hover-bg: #c1d6cc; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-info { - --bs-table-color: #000; - --bs-table-bg: #cff4fc; - --bs-table-border-color: #a6c3ca; - --bs-table-striped-bg: #c5e8ef; - --bs-table-striped-color: #000; - --bs-table-active-bg: #badce3; - --bs-table-active-color: #000; - --bs-table-hover-bg: #bfe2e9; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #cff4fc; + --bs-table-border-color: #a6c3ca; + --bs-table-striped-bg: #c5e8ef; + --bs-table-striped-color: #000; + --bs-table-active-bg: #badce3; + --bs-table-active-color: #000; + --bs-table-hover-bg: #bfe2e9; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-warning { - --bs-table-color: #000; - --bs-table-bg: #fff3cd; - --bs-table-border-color: #ccc2a4; - --bs-table-striped-bg: #f2e7c3; - --bs-table-striped-color: #000; - --bs-table-active-bg: #e6dbb9; - --bs-table-active-color: #000; - --bs-table-hover-bg: #ece1be; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #fff3cd; + --bs-table-border-color: #ccc2a4; + --bs-table-striped-bg: #f2e7c3; + --bs-table-striped-color: #000; + --bs-table-active-bg: #e6dbb9; + --bs-table-active-color: #000; + --bs-table-hover-bg: #ece1be; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-danger { - --bs-table-color: #000; - --bs-table-bg: #f8d7da; - --bs-table-border-color: #c6acae; - --bs-table-striped-bg: #eccccf; - --bs-table-striped-color: #000; - --bs-table-active-bg: #dfc2c4; - --bs-table-active-color: #000; - --bs-table-hover-bg: #e5c7ca; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #f8d7da; + --bs-table-border-color: #c6acae; + --bs-table-striped-bg: #eccccf; + --bs-table-striped-color: #000; + --bs-table-active-bg: #dfc2c4; + --bs-table-active-color: #000; + --bs-table-hover-bg: #e5c7ca; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-light { - --bs-table-color: #000; - --bs-table-bg: #f8f9fa; - --bs-table-border-color: #c6c7c8; - --bs-table-striped-bg: #ecedee; - --bs-table-striped-color: #000; - --bs-table-active-bg: #dfe0e1; - --bs-table-active-color: #000; - --bs-table-hover-bg: #e5e6e7; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #f8f9fa; + --bs-table-border-color: #c6c7c8; + --bs-table-striped-bg: #ecedee; + --bs-table-striped-color: #000; + --bs-table-active-bg: #dfe0e1; + --bs-table-active-color: #000; + --bs-table-hover-bg: #e5e6e7; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-dark { - --bs-table-color: #fff; - --bs-table-bg: #212529; - --bs-table-border-color: #4d5154; - --bs-table-striped-bg: #2c3034; - --bs-table-striped-color: #fff; - --bs-table-active-bg: #373b3e; - --bs-table-active-color: #fff; - --bs-table-hover-bg: #323539; - --bs-table-hover-color: #fff; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #fff; + --bs-table-bg: #212529; + --bs-table-border-color: #4d5154; + --bs-table-striped-bg: #2c3034; + --bs-table-striped-color: #fff; + --bs-table-active-bg: #373b3e; + --bs-table-active-color: #fff; + --bs-table-hover-bg: #323539; + --bs-table-hover-color: #fff; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-responsive { - overflow-x: auto; - -webkit-overflow-scrolling: touch; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } @media (max-width: 575.98px) { - .table-responsive-sm { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-sm { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 767.98px) { - .table-responsive-md { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-md { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 991.98px) { - .table-responsive-lg { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-lg { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 1199.98px) { - .table-responsive-xl { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-xl { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 1399.98px) { - .table-responsive-xxl { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-xxl { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + .form-label { - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } .col-form-label { - padding-top: calc(0.375rem + var(--bs-border-width)); - padding-bottom: calc(0.375rem + var(--bs-border-width)); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5; + padding-top: calc(0.375rem + var(--bs-border-width)); + padding-bottom: calc(0.375rem + var(--bs-border-width)); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; } .col-form-label-lg { - padding-top: calc(0.5rem + var(--bs-border-width)); - padding-bottom: calc(0.5rem + var(--bs-border-width)); - font-size: 1.25rem; + padding-top: calc(0.5rem + var(--bs-border-width)); + padding-bottom: calc(0.5rem + var(--bs-border-width)); + font-size: 1.25rem; } .col-form-label-sm { - padding-top: calc(0.25rem + var(--bs-border-width)); - padding-bottom: calc(0.25rem + var(--bs-border-width)); - font-size: 0.875rem; + padding-top: calc(0.25rem + var(--bs-border-width)); + padding-bottom: calc(0.25rem + var(--bs-border-width)); + font-size: 0.875rem; } .form-text { - margin-top: 0.25rem; - font-size: 0.875em; - color: var(--bs-secondary-color); + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-secondary-color); } .form-control { - display: block; - width: 100%; - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: var(--bs-body-color); - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: var(--bs-body-bg); - background-clip: padding-box; - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + display: block; + width: 100%; + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-body-bg); + background-clip: padding-box; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-control { - transition: none; - } + .form-control { + transition: none; + } } + .form-control[type=file] { - overflow: hidden; + overflow: hidden; } + .form-control[type=file]:not(:disabled):not([readonly]) { - cursor: pointer; + cursor: pointer; } + .form-control:focus { - color: var(--bs-body-color); - background-color: var(--bs-body-bg); - border-color: #86b7fe; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + color: var(--bs-body-color); + background-color: var(--bs-body-bg); + border-color: #86b7fe; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-control::-webkit-date-and-time-value { - min-width: 85px; - height: 1.5em; - margin: 0; + min-width: 85px; + height: 1.5em; + margin: 0; } + .form-control::-webkit-datetime-edit { - display: block; - padding: 0; + display: block; + padding: 0; } + .form-control::-moz-placeholder { - color: var(--bs-secondary-color); - opacity: 1; + color: var(--bs-secondary-color); + opacity: 1; } + .form-control::placeholder { - color: var(--bs-secondary-color); - opacity: 1; + color: var(--bs-secondary-color); + opacity: 1; } + .form-control:disabled { - background-color: var(--bs-secondary-bg); - opacity: 1; + background-color: var(--bs-secondary-bg); + opacity: 1; } + .form-control::-webkit-file-upload-button { - padding: 0.375rem 0.75rem; - margin: -0.375rem -0.75rem; - -webkit-margin-end: 0.75rem; - margin-inline-end: 0.75rem; - color: var(--bs-body-color); - background-color: var(--bs-tertiary-bg); - pointer-events: none; - border-color: inherit; - border-style: solid; - border-width: 0; - border-inline-end-width: var(--bs-border-width); - border-radius: 0; - -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + padding: 0.375rem 0.75rem; + margin: -0.375rem -0.75rem; + -webkit-margin-end: 0.75rem; + margin-inline-end: 0.75rem; + color: var(--bs-body-color); + background-color: var(--bs-tertiary-bg); + pointer-events: none; + border-color: inherit; + border-style: solid; + border-width: 0; + border-inline-end-width: var(--bs-border-width); + border-radius: 0; + -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + .form-control::file-selector-button { - padding: 0.375rem 0.75rem; - margin: -0.375rem -0.75rem; - -webkit-margin-end: 0.75rem; - margin-inline-end: 0.75rem; - color: var(--bs-body-color); - background-color: var(--bs-tertiary-bg); - pointer-events: none; - border-color: inherit; - border-style: solid; - border-width: 0; - border-inline-end-width: var(--bs-border-width); - border-radius: 0; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + padding: 0.375rem 0.75rem; + margin: -0.375rem -0.75rem; + -webkit-margin-end: 0.75rem; + margin-inline-end: 0.75rem; + color: var(--bs-body-color); + background-color: var(--bs-tertiary-bg); + pointer-events: none; + border-color: inherit; + border-style: solid; + border-width: 0; + border-inline-end-width: var(--bs-border-width); + border-radius: 0; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-control::-webkit-file-upload-button { - -webkit-transition: none; - transition: none; - } - .form-control::file-selector-button { - transition: none; - } + .form-control::-webkit-file-upload-button { + -webkit-transition: none; + transition: none; + } + + .form-control::file-selector-button { + transition: none; + } } + .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } + .form-control:hover:not(:disabled):not([readonly])::file-selector-button { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } .form-control-plaintext { - display: block; - width: 100%; - padding: 0.375rem 0; - margin-bottom: 0; - line-height: 1.5; - color: var(--bs-body-color); - background-color: transparent; - border: solid transparent; - border-width: var(--bs-border-width) 0; + display: block; + width: 100%; + padding: 0.375rem 0; + margin-bottom: 0; + line-height: 1.5; + color: var(--bs-body-color); + background-color: transparent; + border: solid transparent; + border-width: var(--bs-border-width) 0; } + .form-control-plaintext:focus { - outline: 0; + outline: 0; } + .form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { - padding-right: 0; - padding-left: 0; + padding-right: 0; + padding-left: 0; } .form-control-sm { - min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - border-radius: var(--bs-border-radius-sm); + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); } + .form-control-sm::-webkit-file-upload-button { - padding: 0.25rem 0.5rem; - margin: -0.25rem -0.5rem; - -webkit-margin-end: 0.5rem; - margin-inline-end: 0.5rem; + padding: 0.25rem 0.5rem; + margin: -0.25rem -0.5rem; + -webkit-margin-end: 0.5rem; + margin-inline-end: 0.5rem; } + .form-control-sm::file-selector-button { - padding: 0.25rem 0.5rem; - margin: -0.25rem -0.5rem; - -webkit-margin-end: 0.5rem; - margin-inline-end: 0.5rem; + padding: 0.25rem 0.5rem; + margin: -0.25rem -0.5rem; + -webkit-margin-end: 0.5rem; + margin-inline-end: 0.5rem; } .form-control-lg { - min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); - padding: 0.5rem 1rem; - font-size: 1.25rem; - border-radius: var(--bs-border-radius-lg); + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); } + .form-control-lg::-webkit-file-upload-button { - padding: 0.5rem 1rem; - margin: -0.5rem -1rem; - -webkit-margin-end: 1rem; - margin-inline-end: 1rem; + padding: 0.5rem 1rem; + margin: -0.5rem -1rem; + -webkit-margin-end: 1rem; + margin-inline-end: 1rem; } + .form-control-lg::file-selector-button { - padding: 0.5rem 1rem; - margin: -0.5rem -1rem; - -webkit-margin-end: 1rem; - margin-inline-end: 1rem; + padding: 0.5rem 1rem; + margin: -0.5rem -1rem; + -webkit-margin-end: 1rem; + margin-inline-end: 1rem; } textarea.form-control { - min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); + min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); } + textarea.form-control-sm { - min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } + textarea.form-control-lg { - min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-control-color { - width: 3rem; - height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); - padding: 0.375rem; + width: 3rem; + height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); + padding: 0.375rem; } + .form-control-color:not(:disabled):not([readonly]) { - cursor: pointer; + cursor: pointer; } + .form-control-color::-moz-color-swatch { - border: 0 !important; - border-radius: var(--bs-border-radius); + border: 0 !important; + border-radius: var(--bs-border-radius); } + .form-control-color::-webkit-color-swatch { - border: 0 !important; - border-radius: var(--bs-border-radius); + border: 0 !important; + border-radius: var(--bs-border-radius); } + .form-control-color.form-control-sm { - height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); + height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } + .form-control-color.form-control-lg { - height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); + height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-select { - --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); - display: block; - width: 100%; - padding: 0.375rem 2.25rem 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: var(--bs-body-color); - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: var(--bs-body-bg); - background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none); - background-repeat: no-repeat; - background-position: right 0.75rem center; - background-size: 16px 12px; - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); + display: block; + width: 100%; + padding: 0.375rem 2.25rem 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-body-bg); + background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none); + background-repeat: no-repeat; + background-position: right 0.75rem center; + background-size: 16px 12px; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-select { - transition: none; - } + .form-select { + transition: none; + } } + .form-select:focus { - border-color: #86b7fe; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + border-color: #86b7fe; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-select[multiple], .form-select[size]:not([size="1"]) { - padding-right: 0.75rem; - background-image: none; + padding-right: 0.75rem; + background-image: none; } + .form-select:disabled { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } + .form-select:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 var(--bs-body-color); + color: transparent; + text-shadow: 0 0 0 var(--bs-body-color); } .form-select-sm { - padding-top: 0.25rem; - padding-bottom: 0.25rem; - padding-left: 0.5rem; - font-size: 0.875rem; - border-radius: var(--bs-border-radius-sm); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); } .form-select-lg { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - font-size: 1.25rem; - border-radius: var(--bs-border-radius-lg); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); } [data-bs-theme=dark] .form-select { - --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); } .form-check { - display: block; - min-height: 1.5rem; - padding-left: 1.5em; - margin-bottom: 0.125rem; + display: block; + min-height: 1.5rem; + padding-left: 1.5em; + margin-bottom: 0.125rem; } + .form-check .form-check-input { - float: left; - margin-left: -1.5em; + float: left; + margin-left: -1.5em; } .form-check-reverse { - padding-right: 1.5em; - padding-left: 0; - text-align: right; + padding-right: 1.5em; + padding-left: 0; + text-align: right; } + .form-check-reverse .form-check-input { - float: right; - margin-right: -1.5em; - margin-left: 0; + float: right; + margin-right: -1.5em; + margin-left: 0; } .form-check-input { - --bs-form-check-bg: var(--bs-body-bg); - flex-shrink: 0; - width: 1em; - height: 1em; - margin-top: 0.25em; - vertical-align: top; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: var(--bs-form-check-bg); - background-image: var(--bs-form-check-bg-image); - background-repeat: no-repeat; - background-position: center; - background-size: contain; - border: var(--bs-border-width) solid var(--bs-border-color); - -webkit-print-color-adjust: exact; - color-adjust: exact; - print-color-adjust: exact; + --bs-form-check-bg: var(--bs-body-bg); + flex-shrink: 0; + width: 1em; + height: 1em; + margin-top: 0.25em; + vertical-align: top; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-form-check-bg); + background-image: var(--bs-form-check-bg-image); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + border: var(--bs-border-width) solid var(--bs-border-color); + -webkit-print-color-adjust: exact; + color-adjust: exact; + print-color-adjust: exact; } + .form-check-input[type=checkbox] { - border-radius: 0.25em; + border-radius: 0.25em; } + .form-check-input[type=radio] { - border-radius: 50%; + border-radius: 50%; } + .form-check-input:active { - filter: brightness(90%); + filter: brightness(90%); } + .form-check-input:focus { - border-color: #86b7fe; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + border-color: #86b7fe; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-check-input:checked { - background-color: #0d6efd; - border-color: #0d6efd; + background-color: #0d6efd; + border-color: #0d6efd; } + .form-check-input:checked[type=checkbox] { - --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); } + .form-check-input:checked[type=radio] { - --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } + .form-check-input[type=checkbox]:indeterminate { - background-color: #0d6efd; - border-color: #0d6efd; - --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); + background-color: #0d6efd; + border-color: #0d6efd; + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); } + .form-check-input:disabled { - pointer-events: none; - filter: none; - opacity: 0.5; + pointer-events: none; + filter: none; + opacity: 0.5; } + .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { - cursor: default; - opacity: 0.5; + cursor: default; + opacity: 0.5; } .form-switch { - padding-left: 2.5em; + padding-left: 2.5em; } + .form-switch .form-check-input { - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); - width: 2em; - margin-left: -2.5em; - background-image: var(--bs-form-switch-bg); - background-position: left center; - border-radius: 2em; - transition: background-position 0.15s ease-in-out; + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); + width: 2em; + margin-left: -2.5em; + background-image: var(--bs-form-switch-bg); + background-position: left center; + border-radius: 2em; + transition: background-position 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-switch .form-check-input { - transition: none; - } + .form-switch .form-check-input { + transition: none; + } } + .form-switch .form-check-input:focus { - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); } + .form-switch .form-check-input:checked { - background-position: right center; - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); + background-position: right center; + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } + .form-switch.form-check-reverse { - padding-right: 2.5em; - padding-left: 0; + padding-right: 2.5em; + padding-left: 0; } + .form-switch.form-check-reverse .form-check-input { - margin-right: -2.5em; - margin-left: 0; + margin-right: -2.5em; + margin-left: 0; } .form-check-inline { - display: inline-block; - margin-right: 1rem; + display: inline-block; + margin-right: 1rem; } .btn-check { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; } + .btn-check[disabled] + .btn, .btn-check:disabled + .btn { - pointer-events: none; - filter: none; - opacity: 0.65; + pointer-events: none; + filter: none; + opacity: 0.65; } [data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus) { - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e"); + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e"); } .form-range { - width: 100%; - height: 1.5rem; - padding: 0; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: transparent; + width: 100%; + height: 1.5rem; + padding: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; } + .form-range:focus { - outline: 0; + outline: 0; } + .form-range:focus::-webkit-slider-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-range:focus::-moz-range-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-range::-moz-focus-outer { - border: 0; + border: 0; } + .form-range::-webkit-slider-thumb { - width: 1rem; - height: 1rem; - margin-top: -0.25rem; - -webkit-appearance: none; - appearance: none; - background-color: #0d6efd; - border: 0; - border-radius: 1rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + -webkit-appearance: none; + appearance: none; + background-color: #0d6efd; + border: 0; + border-radius: 1rem; + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-range::-webkit-slider-thumb { - -webkit-transition: none; - transition: none; - } + .form-range::-webkit-slider-thumb { + -webkit-transition: none; + transition: none; + } } + .form-range::-webkit-slider-thumb:active { - background-color: #b6d4fe; + background-color: #b6d4fe; } + .form-range::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: var(--bs-secondary-bg); - border-color: transparent; - border-radius: 1rem; + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: var(--bs-secondary-bg); + border-color: transparent; + border-radius: 1rem; } + .form-range::-moz-range-thumb { - width: 1rem; - height: 1rem; - -moz-appearance: none; - appearance: none; - background-color: #0d6efd; - border: 0; - border-radius: 1rem; - -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + width: 1rem; + height: 1rem; + -moz-appearance: none; + appearance: none; + background-color: #0d6efd; + border: 0; + border-radius: 1rem; + -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-range::-moz-range-thumb { - -moz-transition: none; - transition: none; - } + .form-range::-moz-range-thumb { + -moz-transition: none; + transition: none; + } } + .form-range::-moz-range-thumb:active { - background-color: #b6d4fe; + background-color: #b6d4fe; } + .form-range::-moz-range-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: var(--bs-secondary-bg); - border-color: transparent; - border-radius: 1rem; + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: var(--bs-secondary-bg); + border-color: transparent; + border-radius: 1rem; } + .form-range:disabled { - pointer-events: none; + pointer-events: none; } + .form-range:disabled::-webkit-slider-thumb { - background-color: var(--bs-secondary-color); + background-color: var(--bs-secondary-color); } + .form-range:disabled::-moz-range-thumb { - background-color: var(--bs-secondary-color); + background-color: var(--bs-secondary-color); } .form-floating { - position: relative; + position: relative; } + .form-floating > .form-control, .form-floating > .form-control-plaintext, .form-floating > .form-select { - height: calc(3.5rem + calc(var(--bs-border-width) * 2)); - min-height: calc(3.5rem + calc(var(--bs-border-width) * 2)); - line-height: 1.25; + height: calc(3.5rem + calc(var(--bs-border-width) * 2)); + min-height: calc(3.5rem + calc(var(--bs-border-width) * 2)); + line-height: 1.25; } + .form-floating > label { - position: absolute; - top: 0; - left: 0; - z-index: 2; - height: 100%; - padding: 1rem 0.75rem; - overflow: hidden; - text-align: start; - text-overflow: ellipsis; - white-space: nowrap; - pointer-events: none; - border: var(--bs-border-width) solid transparent; - transform-origin: 0 0; - transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; + position: absolute; + top: 0; + left: 0; + z-index: 2; + height: 100%; + padding: 1rem 0.75rem; + overflow: hidden; + text-align: start; + text-overflow: ellipsis; + white-space: nowrap; + pointer-events: none; + border: var(--bs-border-width) solid transparent; + transform-origin: 0 0; + transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-floating > label { - transition: none; - } + .form-floating > label { + transition: none; + } } + .form-floating > .form-control, .form-floating > .form-control-plaintext { - padding: 1rem 0.75rem; + padding: 1rem 0.75rem; } + .form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder { - color: transparent; + color: transparent; } + .form-floating > .form-control::placeholder, .form-floating > .form-control-plaintext::placeholder { - color: transparent; + color: transparent; } + .form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown), .form-floating > .form-control-plaintext:focus, .form-floating > .form-control-plaintext:not(:placeholder-shown) { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-control:-webkit-autofill, .form-floating > .form-control-plaintext:-webkit-autofill { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-select { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); - transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } + .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-control-plaintext ~ label, .form-floating > .form-select ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); - transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } + .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label::after { - position: absolute; - inset: 1rem 0.375rem; - z-index: -1; - height: 1.5em; - content: ""; - background-color: var(--bs-body-bg); - border-radius: var(--bs-border-radius); + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; + height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); } + .form-floating > .form-control:focus ~ label::after, .form-floating > .form-control:not(:placeholder-shown) ~ label::after, .form-floating > .form-control-plaintext ~ label::after, .form-floating > .form-select ~ label::after { - position: absolute; - inset: 1rem 0.375rem; - z-index: -1; - height: 1.5em; - content: ""; - background-color: var(--bs-body-bg); - border-radius: var(--bs-border-radius); + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; + height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); } + .form-floating > .form-control:-webkit-autofill ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); - transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } + .form-floating > .form-control-plaintext ~ label { - border-width: var(--bs-border-width) 0; + border-width: var(--bs-border-width) 0; } + .form-floating > :disabled ~ label, .form-floating > .form-control:disabled ~ label { - color: #6c757d; + color: #6c757d; } + .form-floating > :disabled ~ label::after, .form-floating > .form-control:disabled ~ label::after { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } .input-group { - position: relative; - display: flex; - flex-wrap: wrap; - align-items: stretch; - width: 100%; + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100%; } + .input-group > .form-control, .input-group > .form-select, .input-group > .form-floating { - position: relative; - flex: 1 1 auto; - width: 1%; - min-width: 0; + position: relative; + flex: 1 1 auto; + width: 1%; + min-width: 0; } + .input-group > .form-control:focus, .input-group > .form-select:focus, .input-group > .form-floating:focus-within { - z-index: 5; + z-index: 5; } + .input-group .btn { - position: relative; - z-index: 2; + position: relative; + z-index: 2; } + .input-group .btn:focus { - z-index: 5; + z-index: 5; } .input-group-text { - display: flex; - align-items: center; - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: var(--bs-body-color); - text-align: center; - white-space: nowrap; - background-color: var(--bs-tertiary-bg); - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); + display: flex; + align-items: center; + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + text-align: center; + white-space: nowrap; + background-color: var(--bs-tertiary-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); } .input-group-lg > .form-control, .input-group-lg > .form-select, .input-group-lg > .input-group-text, .input-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - border-radius: var(--bs-border-radius-lg); + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); } .input-group-sm > .form-control, .input-group-sm > .form-select, .input-group-sm > .input-group-text, .input-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - border-radius: var(--bs-border-radius-sm); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); } .input-group-lg > .form-select, .input-group-sm > .form-select { - padding-right: 3rem; + padding-right: 3rem; } .input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), .input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3), .input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control, .input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select { - border-top-right-radius: 0; - border-bottom-right-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), .input-group.has-validation > .dropdown-toggle:nth-last-child(n+4), .input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-control, .input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-select { - border-top-right-radius: 0; - border-bottom-right-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { - margin-left: calc(var(--bs-border-width) * -1); - border-top-left-radius: 0; - border-bottom-left-radius: 0; + margin-left: calc(var(--bs-border-width) * -1); + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .input-group > .form-floating:not(:first-child) > .form-control, .input-group > .form-floating:not(:first-child) > .form-select { - border-top-left-radius: 0; - border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; } .valid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 0.875em; - color: var(--bs-form-valid-color); + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-form-valid-color); } .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: 0.1rem; - font-size: 0.875rem; - color: #fff; - background-color: var(--bs-success); - border-radius: var(--bs-border-radius); + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + color: #fff; + background-color: var(--bs-success); + border-radius: var(--bs-border-radius); } .was-validated :valid ~ .valid-feedback, .was-validated :valid ~ .valid-tooltip, .is-valid ~ .valid-feedback, .is-valid ~ .valid-tooltip { - display: block; + display: block; } .was-validated .form-control:valid, .form-control.is-valid { - border-color: var(--bs-form-valid-border-color); - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + border-color: var(--bs-form-valid-border-color); + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: var(--bs-form-valid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .was-validated .form-select:valid, .form-select.is-valid { - border-color: var(--bs-form-valid-border-color); + border-color: var(--bs-form-valid-border-color); } + .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { - --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - padding-right: 4.125rem; - background-position: right 0.75rem center, center right 2.25rem; - background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + padding-right: 4.125rem; + background-position: right 0.75rem center, center right 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-select:valid:focus, .form-select.is-valid:focus { - border-color: var(--bs-form-valid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated .form-control-color:valid, .form-control-color.is-valid { - width: calc(3rem + calc(1.5em + 0.75rem)); + width: calc(3rem + calc(1.5em + 0.75rem)); } .was-validated .form-check-input:valid, .form-check-input.is-valid { - border-color: var(--bs-form-valid-border-color); + border-color: var(--bs-form-valid-border-color); } + .was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { - background-color: var(--bs-form-valid-color); + background-color: var(--bs-form-valid-color); } + .was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { - box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } + .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: var(--bs-form-valid-color); + color: var(--bs-form-valid-color); } .form-check-inline .form-check-input ~ .valid-feedback { - margin-left: 0.5em; + margin-left: 0.5em; } .was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid, @@ -2879,89 +3239,95 @@ textarea.form-control-lg { .input-group > .form-select:not(:focus).is-valid, .was-validated .input-group > .form-floating:not(:focus-within):valid, .input-group > .form-floating:not(:focus-within).is-valid { - z-index: 3; + z-index: 3; } .invalid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 0.875em; - color: var(--bs-form-invalid-color); + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-form-invalid-color); } .invalid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: 0.1rem; - font-size: 0.875rem; - color: #fff; - background-color: var(--bs-danger); - border-radius: var(--bs-border-radius); + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + color: #fff; + background-color: var(--bs-danger); + border-radius: var(--bs-border-radius); } .was-validated :invalid ~ .invalid-feedback, .was-validated :invalid ~ .invalid-tooltip, .is-invalid ~ .invalid-feedback, .is-invalid ~ .invalid-tooltip { - display: block; + display: block; } .was-validated .form-control:invalid, .form-control.is-invalid { - border-color: var(--bs-form-invalid-border-color); - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + border-color: var(--bs-form-invalid-border-color); + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: var(--bs-form-invalid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } .was-validated .form-select:invalid, .form-select.is-invalid { - border-color: var(--bs-form-invalid-border-color); + border-color: var(--bs-form-invalid-border-color); } + .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { - --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); - padding-right: 4.125rem; - background-position: right 0.75rem center, center right 2.25rem; - background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + padding-right: 4.125rem; + background-position: right 0.75rem center, center right 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { - border-color: var(--bs-form-invalid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated .form-control-color:invalid, .form-control-color.is-invalid { - width: calc(3rem + calc(1.5em + 0.75rem)); + width: calc(3rem + calc(1.5em + 0.75rem)); } .was-validated .form-check-input:invalid, .form-check-input.is-invalid { - border-color: var(--bs-form-invalid-border-color); + border-color: var(--bs-form-invalid-border-color); } + .was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { - background-color: var(--bs-form-invalid-color); + background-color: var(--bs-form-invalid-color); } + .was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { - box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } + .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: var(--bs-form-invalid-color); + color: var(--bs-form-invalid-color); } .form-check-inline .form-check-input ~ .invalid-feedback { - margin-left: 0.5em; + margin-left: 0.5em; } .was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid, @@ -2969,433 +3335,449 @@ textarea.form-control-lg { .input-group > .form-select:not(:focus).is-invalid, .was-validated .input-group > .form-floating:not(:focus-within):invalid, .input-group > .form-floating:not(:focus-within).is-invalid { - z-index: 4; + z-index: 4; } .btn { - --bs-btn-padding-x: 0.75rem; - --bs-btn-padding-y: 0.375rem; - --bs-btn-font-family: ; - --bs-btn-font-size: 1rem; - --bs-btn-font-weight: 400; - --bs-btn-line-height: 1.5; - --bs-btn-color: var(--bs-body-color); - --bs-btn-bg: transparent; - --bs-btn-border-width: var(--bs-border-width); - --bs-btn-border-color: transparent; - --bs-btn-border-radius: var(--bs-border-radius); - --bs-btn-hover-border-color: transparent; - --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); - --bs-btn-disabled-opacity: 0.65; - --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5); - display: inline-block; - padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x); - font-family: var(--bs-btn-font-family); - font-size: var(--bs-btn-font-size); - font-weight: var(--bs-btn-font-weight); - line-height: var(--bs-btn-line-height); - color: var(--bs-btn-color); - text-align: center; - text-decoration: none; - vertical-align: middle; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); - border-radius: var(--bs-btn-border-radius); - background-color: var(--bs-btn-bg); - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + --bs-btn-padding-x: 0.75rem; + --bs-btn-padding-y: 0.375rem; + --bs-btn-font-family: ; + --bs-btn-font-size: 1rem; + --bs-btn-font-weight: 400; + --bs-btn-line-height: 1.5; + --bs-btn-color: var(--bs-body-color); + --bs-btn-bg: transparent; + --bs-btn-border-width: var(--bs-border-width); + --bs-btn-border-color: transparent; + --bs-btn-border-radius: var(--bs-border-radius); + --bs-btn-hover-border-color: transparent; + --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); + --bs-btn-disabled-opacity: 0.65; + --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5); + display: inline-block; + padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x); + font-family: var(--bs-btn-font-family); + font-size: var(--bs-btn-font-size); + font-weight: var(--bs-btn-font-weight); + line-height: var(--bs-btn-line-height); + color: var(--bs-btn-color); + text-align: center; + text-decoration: none; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); + border-radius: var(--bs-btn-border-radius); + background-color: var(--bs-btn-bg); + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .btn { - transition: none; - } + .btn { + transition: none; + } } + .btn:hover { - color: var(--bs-btn-hover-color); - background-color: var(--bs-btn-hover-bg); - border-color: var(--bs-btn-hover-border-color); + color: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); } + .btn-check + .btn:hover { - color: var(--bs-btn-color); - background-color: var(--bs-btn-bg); - border-color: var(--bs-btn-border-color); + color: var(--bs-btn-color); + background-color: var(--bs-btn-bg); + border-color: var(--bs-btn-border-color); } + .btn:focus-visible { - color: var(--bs-btn-hover-color); - background-color: var(--bs-btn-hover-bg); - border-color: var(--bs-btn-hover-border-color); - outline: 0; - box-shadow: var(--bs-btn-focus-box-shadow); + color: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); + outline: 0; + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn-check:focus-visible + .btn { - border-color: var(--bs-btn-hover-border-color); - outline: 0; - box-shadow: var(--bs-btn-focus-box-shadow); + border-color: var(--bs-btn-hover-border-color); + outline: 0; + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show { - color: var(--bs-btn-active-color); - background-color: var(--bs-btn-active-bg); - border-color: var(--bs-btn-active-border-color); + color: var(--bs-btn-active-color); + background-color: var(--bs-btn-active-bg); + border-color: var(--bs-btn-active-border-color); } + .btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible { - box-shadow: var(--bs-btn-focus-box-shadow); + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn-check:checked:focus-visible + .btn { - box-shadow: var(--bs-btn-focus-box-shadow); + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn:disabled, .btn.disabled, fieldset:disabled .btn { - color: var(--bs-btn-disabled-color); - pointer-events: none; - background-color: var(--bs-btn-disabled-bg); - border-color: var(--bs-btn-disabled-border-color); - opacity: var(--bs-btn-disabled-opacity); + color: var(--bs-btn-disabled-color); + pointer-events: none; + background-color: var(--bs-btn-disabled-bg); + border-color: var(--bs-btn-disabled-border-color); + opacity: var(--bs-btn-disabled-opacity); } .btn-primary { - --bs-btn-color: #fff; - --bs-btn-bg: #0d6efd; - --bs-btn-border-color: #0d6efd; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #0b5ed7; - --bs-btn-hover-border-color: #0a58ca; - --bs-btn-focus-shadow-rgb: 49, 132, 253; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #0a58ca; - --bs-btn-active-border-color: #0a53be; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #0d6efd; - --bs-btn-disabled-border-color: #0d6efd; + --bs-btn-color: #fff; + --bs-btn-bg: #0d6efd; + --bs-btn-border-color: #0d6efd; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #0b5ed7; + --bs-btn-hover-border-color: #0a58ca; + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #0a58ca; + --bs-btn-active-border-color: #0a53be; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #0d6efd; + --bs-btn-disabled-border-color: #0d6efd; } .btn-secondary { - --bs-btn-color: #fff; - --bs-btn-bg: #6c757d; - --bs-btn-border-color: #6c757d; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #5c636a; - --bs-btn-hover-border-color: #565e64; - --bs-btn-focus-shadow-rgb: 130, 138, 145; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #565e64; - --bs-btn-active-border-color: #51585e; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #6c757d; - --bs-btn-disabled-border-color: #6c757d; + --bs-btn-color: #fff; + --bs-btn-bg: #6c757d; + --bs-btn-border-color: #6c757d; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #5c636a; + --bs-btn-hover-border-color: #565e64; + --bs-btn-focus-shadow-rgb: 130, 138, 145; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #565e64; + --bs-btn-active-border-color: #51585e; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #6c757d; + --bs-btn-disabled-border-color: #6c757d; } .btn-success { - --bs-btn-color: #fff; - --bs-btn-bg: #198754; - --bs-btn-border-color: #198754; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #157347; - --bs-btn-hover-border-color: #146c43; - --bs-btn-focus-shadow-rgb: 60, 153, 110; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #146c43; - --bs-btn-active-border-color: #13653f; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #198754; - --bs-btn-disabled-border-color: #198754; + --bs-btn-color: #fff; + --bs-btn-bg: #198754; + --bs-btn-border-color: #198754; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #157347; + --bs-btn-hover-border-color: #146c43; + --bs-btn-focus-shadow-rgb: 60, 153, 110; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #146c43; + --bs-btn-active-border-color: #13653f; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #198754; + --bs-btn-disabled-border-color: #198754; } .btn-info { - --bs-btn-color: #000; - --bs-btn-bg: #0dcaf0; - --bs-btn-border-color: #0dcaf0; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #31d2f2; - --bs-btn-hover-border-color: #25cff2; - --bs-btn-focus-shadow-rgb: 11, 172, 204; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #3dd5f3; - --bs-btn-active-border-color: #25cff2; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #000; - --bs-btn-disabled-bg: #0dcaf0; - --bs-btn-disabled-border-color: #0dcaf0; + --bs-btn-color: #000; + --bs-btn-bg: #0dcaf0; + --bs-btn-border-color: #0dcaf0; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #31d2f2; + --bs-btn-hover-border-color: #25cff2; + --bs-btn-focus-shadow-rgb: 11, 172, 204; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #3dd5f3; + --bs-btn-active-border-color: #25cff2; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #0dcaf0; + --bs-btn-disabled-border-color: #0dcaf0; } .btn-warning { - --bs-btn-color: #000; - --bs-btn-bg: #ffc107; - --bs-btn-border-color: #ffc107; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #ffca2c; - --bs-btn-hover-border-color: #ffc720; - --bs-btn-focus-shadow-rgb: 217, 164, 6; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #ffcd39; - --bs-btn-active-border-color: #ffc720; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #000; - --bs-btn-disabled-bg: #ffc107; - --bs-btn-disabled-border-color: #ffc107; + --bs-btn-color: #000; + --bs-btn-bg: #ffc107; + --bs-btn-border-color: #ffc107; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #ffca2c; + --bs-btn-hover-border-color: #ffc720; + --bs-btn-focus-shadow-rgb: 217, 164, 6; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #ffcd39; + --bs-btn-active-border-color: #ffc720; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #ffc107; + --bs-btn-disabled-border-color: #ffc107; } .btn-danger { - --bs-btn-color: #fff; - --bs-btn-bg: #dc3545; - --bs-btn-border-color: #dc3545; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #bb2d3b; - --bs-btn-hover-border-color: #b02a37; - --bs-btn-focus-shadow-rgb: 225, 83, 97; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #b02a37; - --bs-btn-active-border-color: #a52834; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #dc3545; - --bs-btn-disabled-border-color: #dc3545; + --bs-btn-color: #fff; + --bs-btn-bg: #dc3545; + --bs-btn-border-color: #dc3545; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #bb2d3b; + --bs-btn-hover-border-color: #b02a37; + --bs-btn-focus-shadow-rgb: 225, 83, 97; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #b02a37; + --bs-btn-active-border-color: #a52834; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #dc3545; + --bs-btn-disabled-border-color: #dc3545; } .btn-light { - --bs-btn-color: #000; - --bs-btn-bg: #f8f9fa; - --bs-btn-border-color: #f8f9fa; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #d3d4d5; - --bs-btn-hover-border-color: #c6c7c8; - --bs-btn-focus-shadow-rgb: 211, 212, 213; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #c6c7c8; - --bs-btn-active-border-color: #babbbc; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #000; - --bs-btn-disabled-bg: #f8f9fa; - --bs-btn-disabled-border-color: #f8f9fa; + --bs-btn-color: #000; + --bs-btn-bg: #f8f9fa; + --bs-btn-border-color: #f8f9fa; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #d3d4d5; + --bs-btn-hover-border-color: #c6c7c8; + --bs-btn-focus-shadow-rgb: 211, 212, 213; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #c6c7c8; + --bs-btn-active-border-color: #babbbc; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #f8f9fa; + --bs-btn-disabled-border-color: #f8f9fa; } .btn-dark { - --bs-btn-color: #fff; - --bs-btn-bg: #212529; - --bs-btn-border-color: #212529; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #424649; - --bs-btn-hover-border-color: #373b3e; - --bs-btn-focus-shadow-rgb: 66, 70, 73; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #4d5154; - --bs-btn-active-border-color: #373b3e; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #212529; - --bs-btn-disabled-border-color: #212529; + --bs-btn-color: #fff; + --bs-btn-bg: #212529; + --bs-btn-border-color: #212529; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #424649; + --bs-btn-hover-border-color: #373b3e; + --bs-btn-focus-shadow-rgb: 66, 70, 73; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #4d5154; + --bs-btn-active-border-color: #373b3e; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #212529; + --bs-btn-disabled-border-color: #212529; } .btn-outline-primary { - --bs-btn-color: #0d6efd; - --bs-btn-border-color: #0d6efd; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #0d6efd; - --bs-btn-hover-border-color: #0d6efd; - --bs-btn-focus-shadow-rgb: 13, 110, 253; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #0d6efd; - --bs-btn-active-border-color: #0d6efd; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #0d6efd; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #0d6efd; - --bs-gradient: none; + --bs-btn-color: #0d6efd; + --bs-btn-border-color: #0d6efd; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #0d6efd; + --bs-btn-hover-border-color: #0d6efd; + --bs-btn-focus-shadow-rgb: 13, 110, 253; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #0d6efd; + --bs-btn-active-border-color: #0d6efd; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #0d6efd; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #0d6efd; + --bs-gradient: none; } .btn-outline-secondary { - --bs-btn-color: #6c757d; - --bs-btn-border-color: #6c757d; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #6c757d; - --bs-btn-hover-border-color: #6c757d; - --bs-btn-focus-shadow-rgb: 108, 117, 125; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #6c757d; - --bs-btn-active-border-color: #6c757d; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #6c757d; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #6c757d; - --bs-gradient: none; + --bs-btn-color: #6c757d; + --bs-btn-border-color: #6c757d; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #6c757d; + --bs-btn-hover-border-color: #6c757d; + --bs-btn-focus-shadow-rgb: 108, 117, 125; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #6c757d; + --bs-btn-active-border-color: #6c757d; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #6c757d; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #6c757d; + --bs-gradient: none; } .btn-outline-success { - --bs-btn-color: #198754; - --bs-btn-border-color: #198754; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #198754; - --bs-btn-hover-border-color: #198754; - --bs-btn-focus-shadow-rgb: 25, 135, 84; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #198754; - --bs-btn-active-border-color: #198754; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #198754; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #198754; - --bs-gradient: none; + --bs-btn-color: #198754; + --bs-btn-border-color: #198754; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #198754; + --bs-btn-hover-border-color: #198754; + --bs-btn-focus-shadow-rgb: 25, 135, 84; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #198754; + --bs-btn-active-border-color: #198754; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #198754; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #198754; + --bs-gradient: none; } .btn-outline-info { - --bs-btn-color: #0dcaf0; - --bs-btn-border-color: #0dcaf0; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #0dcaf0; - --bs-btn-hover-border-color: #0dcaf0; - --bs-btn-focus-shadow-rgb: 13, 202, 240; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #0dcaf0; - --bs-btn-active-border-color: #0dcaf0; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #0dcaf0; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #0dcaf0; - --bs-gradient: none; + --bs-btn-color: #0dcaf0; + --bs-btn-border-color: #0dcaf0; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #0dcaf0; + --bs-btn-hover-border-color: #0dcaf0; + --bs-btn-focus-shadow-rgb: 13, 202, 240; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #0dcaf0; + --bs-btn-active-border-color: #0dcaf0; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #0dcaf0; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #0dcaf0; + --bs-gradient: none; } .btn-outline-warning { - --bs-btn-color: #ffc107; - --bs-btn-border-color: #ffc107; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #ffc107; - --bs-btn-hover-border-color: #ffc107; - --bs-btn-focus-shadow-rgb: 255, 193, 7; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #ffc107; - --bs-btn-active-border-color: #ffc107; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #ffc107; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #ffc107; - --bs-gradient: none; + --bs-btn-color: #ffc107; + --bs-btn-border-color: #ffc107; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #ffc107; + --bs-btn-hover-border-color: #ffc107; + --bs-btn-focus-shadow-rgb: 255, 193, 7; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #ffc107; + --bs-btn-active-border-color: #ffc107; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #ffc107; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #ffc107; + --bs-gradient: none; } .btn-outline-danger { - --bs-btn-color: #dc3545; - --bs-btn-border-color: #dc3545; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #dc3545; - --bs-btn-hover-border-color: #dc3545; - --bs-btn-focus-shadow-rgb: 220, 53, 69; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #dc3545; - --bs-btn-active-border-color: #dc3545; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #dc3545; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #dc3545; - --bs-gradient: none; + --bs-btn-color: #dc3545; + --bs-btn-border-color: #dc3545; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #dc3545; + --bs-btn-hover-border-color: #dc3545; + --bs-btn-focus-shadow-rgb: 220, 53, 69; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #dc3545; + --bs-btn-active-border-color: #dc3545; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #dc3545; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #dc3545; + --bs-gradient: none; } .btn-outline-light { - --bs-btn-color: #f8f9fa; - --bs-btn-border-color: #f8f9fa; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #f8f9fa; - --bs-btn-hover-border-color: #f8f9fa; - --bs-btn-focus-shadow-rgb: 248, 249, 250; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #f8f9fa; - --bs-btn-active-border-color: #f8f9fa; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #f8f9fa; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #f8f9fa; - --bs-gradient: none; + --bs-btn-color: #f8f9fa; + --bs-btn-border-color: #f8f9fa; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #f8f9fa; + --bs-btn-hover-border-color: #f8f9fa; + --bs-btn-focus-shadow-rgb: 248, 249, 250; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #f8f9fa; + --bs-btn-active-border-color: #f8f9fa; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #f8f9fa; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #f8f9fa; + --bs-gradient: none; } .btn-outline-dark { - --bs-btn-color: #212529; - --bs-btn-border-color: #212529; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #212529; - --bs-btn-hover-border-color: #212529; - --bs-btn-focus-shadow-rgb: 33, 37, 41; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #212529; - --bs-btn-active-border-color: #212529; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #212529; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #212529; - --bs-gradient: none; + --bs-btn-color: #212529; + --bs-btn-border-color: #212529; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #212529; + --bs-btn-hover-border-color: #212529; + --bs-btn-focus-shadow-rgb: 33, 37, 41; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #212529; + --bs-btn-active-border-color: #212529; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #212529; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #212529; + --bs-gradient: none; } .btn-link { - --bs-btn-font-weight: 400; - --bs-btn-color: var(--bs-link-color); - --bs-btn-bg: transparent; - --bs-btn-border-color: transparent; - --bs-btn-hover-color: var(--bs-link-hover-color); - --bs-btn-hover-border-color: transparent; - --bs-btn-active-color: var(--bs-link-hover-color); - --bs-btn-active-border-color: transparent; - --bs-btn-disabled-color: #6c757d; - --bs-btn-disabled-border-color: transparent; - --bs-btn-box-shadow: 0 0 0 #000; - --bs-btn-focus-shadow-rgb: 49, 132, 253; - text-decoration: underline; + --bs-btn-font-weight: 400; + --bs-btn-color: var(--bs-link-color); + --bs-btn-bg: transparent; + --bs-btn-border-color: transparent; + --bs-btn-hover-color: var(--bs-link-hover-color); + --bs-btn-hover-border-color: transparent; + --bs-btn-active-color: var(--bs-link-hover-color); + --bs-btn-active-border-color: transparent; + --bs-btn-disabled-color: #6c757d; + --bs-btn-disabled-border-color: transparent; + --bs-btn-box-shadow: 0 0 0 #000; + --bs-btn-focus-shadow-rgb: 49, 132, 253; + text-decoration: underline; } + .btn-link:focus-visible { - color: var(--bs-btn-color); + color: var(--bs-btn-color); } + .btn-link:hover { - color: var(--bs-btn-hover-color); + color: var(--bs-btn-hover-color); } .btn-lg, .btn-group-lg > .btn { - --bs-btn-padding-y: 0.5rem; - --bs-btn-padding-x: 1rem; - --bs-btn-font-size: 1.25rem; - --bs-btn-border-radius: var(--bs-border-radius-lg); + --bs-btn-padding-y: 0.5rem; + --bs-btn-padding-x: 1rem; + --bs-btn-font-size: 1.25rem; + --bs-btn-border-radius: var(--bs-border-radius-lg); } .btn-sm, .btn-group-sm > .btn { - --bs-btn-padding-y: 0.25rem; - --bs-btn-padding-x: 0.5rem; - --bs-btn-font-size: 0.875rem; - --bs-btn-border-radius: var(--bs-border-radius-sm); + --bs-btn-padding-y: 0.25rem; + --bs-btn-padding-x: 0.5rem; + --bs-btn-font-size: 0.875rem; + --bs-btn-border-radius: var(--bs-border-radius-sm); } .fade { - transition: opacity 0.15s linear; + transition: opacity 0.15s linear; } + @media (prefers-reduced-motion: reduce) { - .fade { - transition: none; - } + .fade { + transition: none; + } } + .fade:not(.show) { - opacity: 0; + opacity: 0; } .collapse:not(.show) { - display: none; + display: none; } .collapsing { - height: 0; - overflow: hidden; - transition: height 0.35s ease; + height: 0; + overflow: hidden; + transition: height 0.35s ease; } + @media (prefers-reduced-motion: reduce) { - .collapsing { - transition: none; - } + .collapsing { + transition: none; + } } + .collapsing.collapse-horizontal { - width: 0; - height: auto; - transition: width 0.35s ease; + width: 0; + height: auto; + transition: width 0.35s ease; } + @media (prefers-reduced-motion: reduce) { - .collapsing.collapse-horizontal { - transition: none; - } + .collapsing.collapse-horizontal { + transition: none; + } } .dropup, @@ -3404,328 +3786,368 @@ textarea.form-control-lg { .dropstart, .dropup-center, .dropdown-center { - position: relative; + position: relative; } .dropdown-toggle { - white-space: nowrap; + white-space: nowrap; } + .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid; - border-right: 0.3em solid transparent; - border-bottom: 0; - border-left: 0.3em solid transparent; + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; } + .dropdown-toggle:empty::after { - margin-left: 0; + margin-left: 0; } .dropdown-menu { - --bs-dropdown-zindex: 1000; - --bs-dropdown-min-width: 10rem; - --bs-dropdown-padding-x: 0; - --bs-dropdown-padding-y: 0.5rem; - --bs-dropdown-spacer: 0.125rem; - --bs-dropdown-font-size: 1rem; - --bs-dropdown-color: var(--bs-body-color); - --bs-dropdown-bg: var(--bs-body-bg); - --bs-dropdown-border-color: var(--bs-border-color-translucent); - --bs-dropdown-border-radius: var(--bs-border-radius); - --bs-dropdown-border-width: var(--bs-border-width); - --bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width)); - --bs-dropdown-divider-bg: var(--bs-border-color-translucent); - --bs-dropdown-divider-margin-y: 0.5rem; - --bs-dropdown-box-shadow: var(--bs-box-shadow); - --bs-dropdown-link-color: var(--bs-body-color); - --bs-dropdown-link-hover-color: var(--bs-body-color); - --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg); - --bs-dropdown-link-active-color: #fff; - --bs-dropdown-link-active-bg: #0d6efd; - --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); - --bs-dropdown-item-padding-x: 1rem; - --bs-dropdown-item-padding-y: 0.25rem; - --bs-dropdown-header-color: #6c757d; - --bs-dropdown-header-padding-x: 1rem; - --bs-dropdown-header-padding-y: 0.5rem; - position: absolute; - z-index: var(--bs-dropdown-zindex); - display: none; - min-width: var(--bs-dropdown-min-width); - padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); - margin: 0; - font-size: var(--bs-dropdown-font-size); - color: var(--bs-dropdown-color); - text-align: left; - list-style: none; - background-color: var(--bs-dropdown-bg); - background-clip: padding-box; - border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); - border-radius: var(--bs-dropdown-border-radius); + --bs-dropdown-zindex: 1000; + --bs-dropdown-min-width: 10rem; + --bs-dropdown-padding-x: 0; + --bs-dropdown-padding-y: 0.5rem; + --bs-dropdown-spacer: 0.125rem; + --bs-dropdown-font-size: 1rem; + --bs-dropdown-color: var(--bs-body-color); + --bs-dropdown-bg: var(--bs-body-bg); + --bs-dropdown-border-color: var(--bs-border-color-translucent); + --bs-dropdown-border-radius: var(--bs-border-radius); + --bs-dropdown-border-width: var(--bs-border-width); + --bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width)); + --bs-dropdown-divider-bg: var(--bs-border-color-translucent); + --bs-dropdown-divider-margin-y: 0.5rem; + --bs-dropdown-box-shadow: var(--bs-box-shadow); + --bs-dropdown-link-color: var(--bs-body-color); + --bs-dropdown-link-hover-color: var(--bs-body-color); + --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg); + --bs-dropdown-link-active-color: #fff; + --bs-dropdown-link-active-bg: #0d6efd; + --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); + --bs-dropdown-item-padding-x: 1rem; + --bs-dropdown-item-padding-y: 0.25rem; + --bs-dropdown-header-color: #6c757d; + --bs-dropdown-header-padding-x: 1rem; + --bs-dropdown-header-padding-y: 0.5rem; + position: absolute; + z-index: var(--bs-dropdown-zindex); + display: none; + min-width: var(--bs-dropdown-min-width); + padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); + margin: 0; + font-size: var(--bs-dropdown-font-size); + color: var(--bs-dropdown-color); + text-align: left; + list-style: none; + background-color: var(--bs-dropdown-bg); + background-clip: padding-box; + border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); + border-radius: var(--bs-dropdown-border-radius); } + .dropdown-menu[data-bs-popper] { - top: 100%; - left: 0; - margin-top: var(--bs-dropdown-spacer); + top: 100%; + left: 0; + margin-top: var(--bs-dropdown-spacer); } .dropdown-menu-start { - --bs-position: start; + --bs-position: start; } + .dropdown-menu-start[data-bs-popper] { - right: auto; - left: 0; + right: auto; + left: 0; } .dropdown-menu-end { - --bs-position: end; + --bs-position: end; } + .dropdown-menu-end[data-bs-popper] { - right: 0; - left: auto; + right: 0; + left: auto; } @media (min-width: 576px) { - .dropdown-menu-sm-start { - --bs-position: start; - } - .dropdown-menu-sm-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-sm-end { - --bs-position: end; - } - .dropdown-menu-sm-end[data-bs-popper] { - right: 0; - left: auto; - } + .dropdown-menu-sm-start { + --bs-position: start; + } + + .dropdown-menu-sm-start[data-bs-popper] { + right: auto; + left: 0; + } + + .dropdown-menu-sm-end { + --bs-position: end; + } + + .dropdown-menu-sm-end[data-bs-popper] { + right: 0; + left: auto; + } } + @media (min-width: 768px) { - .dropdown-menu-md-start { - --bs-position: start; - } - .dropdown-menu-md-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-md-end { - --bs-position: end; - } - .dropdown-menu-md-end[data-bs-popper] { - right: 0; - left: auto; - } + .dropdown-menu-md-start { + --bs-position: start; + } + + .dropdown-menu-md-start[data-bs-popper] { + right: auto; + left: 0; + } + + .dropdown-menu-md-end { + --bs-position: end; + } + + .dropdown-menu-md-end[data-bs-popper] { + right: 0; + left: auto; + } } + @media (min-width: 992px) { - .dropdown-menu-lg-start { - --bs-position: start; - } - .dropdown-menu-lg-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-lg-end { - --bs-position: end; - } - .dropdown-menu-lg-end[data-bs-popper] { - right: 0; - left: auto; - } + .dropdown-menu-lg-start { + --bs-position: start; + } + + .dropdown-menu-lg-start[data-bs-popper] { + right: auto; + left: 0; + } + + .dropdown-menu-lg-end { + --bs-position: end; + } + + .dropdown-menu-lg-end[data-bs-popper] { + right: 0; + left: auto; + } } + @media (min-width: 1200px) { - .dropdown-menu-xl-start { - --bs-position: start; - } - .dropdown-menu-xl-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-xl-end { - --bs-position: end; - } - .dropdown-menu-xl-end[data-bs-popper] { - right: 0; - left: auto; - } + .dropdown-menu-xl-start { + --bs-position: start; + } + + .dropdown-menu-xl-start[data-bs-popper] { + right: auto; + left: 0; + } + + .dropdown-menu-xl-end { + --bs-position: end; + } + + .dropdown-menu-xl-end[data-bs-popper] { + right: 0; + left: auto; + } } + @media (min-width: 1400px) { - .dropdown-menu-xxl-start { - --bs-position: start; - } - .dropdown-menu-xxl-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-xxl-end { - --bs-position: end; - } - .dropdown-menu-xxl-end[data-bs-popper] { - right: 0; - left: auto; - } + .dropdown-menu-xxl-start { + --bs-position: start; + } + + .dropdown-menu-xxl-start[data-bs-popper] { + right: auto; + left: 0; + } + + .dropdown-menu-xxl-end { + --bs-position: end; + } + + .dropdown-menu-xxl-end[data-bs-popper] { + right: 0; + left: auto; + } } + .dropup .dropdown-menu[data-bs-popper] { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: var(--bs-dropdown-spacer); + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: var(--bs-dropdown-spacer); } + .dropup .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0; - border-right: 0.3em solid transparent; - border-bottom: 0.3em solid; - border-left: 0.3em solid transparent; + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; } + .dropup .dropdown-toggle:empty::after { - margin-left: 0; + margin-left: 0; } .dropend .dropdown-menu[data-bs-popper] { - top: 0; - right: auto; - left: 100%; - margin-top: 0; - margin-left: var(--bs-dropdown-spacer); + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: var(--bs-dropdown-spacer); } + .dropend .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0; - border-bottom: 0.3em solid transparent; - border-left: 0.3em solid; + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; } + .dropend .dropdown-toggle:empty::after { - margin-left: 0; + margin-left: 0; } + .dropend .dropdown-toggle::after { - vertical-align: 0; + vertical-align: 0; } .dropstart .dropdown-menu[data-bs-popper] { - top: 0; - right: 100%; - left: auto; - margin-top: 0; - margin-right: var(--bs-dropdown-spacer); + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: var(--bs-dropdown-spacer); } + .dropstart .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; } + .dropstart .dropdown-toggle::after { - display: none; + display: none; } + .dropstart .dropdown-toggle::before { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0.3em solid; - border-bottom: 0.3em solid transparent; + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; } + .dropstart .dropdown-toggle:empty::after { - margin-left: 0; + margin-left: 0; } + .dropstart .dropdown-toggle::before { - vertical-align: 0; + vertical-align: 0; } .dropdown-divider { - height: 0; - margin: var(--bs-dropdown-divider-margin-y) 0; - overflow: hidden; - border-top: 1px solid var(--bs-dropdown-divider-bg); - opacity: 1; + height: 0; + margin: var(--bs-dropdown-divider-margin-y) 0; + overflow: hidden; + border-top: 1px solid var(--bs-dropdown-divider-bg); + opacity: 1; } .dropdown-item { - display: block; - width: 100%; - padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); - clear: both; - font-weight: 400; - color: var(--bs-dropdown-link-color); - text-align: inherit; - text-decoration: none; - white-space: nowrap; - background-color: transparent; - border: 0; - border-radius: var(--bs-dropdown-item-border-radius, 0); + display: block; + width: 100%; + padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); + clear: both; + font-weight: 400; + color: var(--bs-dropdown-link-color); + text-align: inherit; + text-decoration: none; + white-space: nowrap; + background-color: transparent; + border: 0; + border-radius: var(--bs-dropdown-item-border-radius, 0); } + .dropdown-item:hover, .dropdown-item:focus { - color: var(--bs-dropdown-link-hover-color); - background-color: var(--bs-dropdown-link-hover-bg); + color: var(--bs-dropdown-link-hover-color); + background-color: var(--bs-dropdown-link-hover-bg); } + .dropdown-item.active, .dropdown-item:active { - color: var(--bs-dropdown-link-active-color); - text-decoration: none; - background-color: var(--bs-dropdown-link-active-bg); + color: var(--bs-dropdown-link-active-color); + text-decoration: none; + background-color: var(--bs-dropdown-link-active-bg); } + .dropdown-item.disabled, .dropdown-item:disabled { - color: var(--bs-dropdown-link-disabled-color); - pointer-events: none; - background-color: transparent; + color: var(--bs-dropdown-link-disabled-color); + pointer-events: none; + background-color: transparent; } .dropdown-menu.show { - display: block; + display: block; } .dropdown-header { - display: block; - padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x); - margin-bottom: 0; - font-size: 0.875rem; - color: var(--bs-dropdown-header-color); - white-space: nowrap; + display: block; + padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x); + margin-bottom: 0; + font-size: 0.875rem; + color: var(--bs-dropdown-header-color); + white-space: nowrap; } .dropdown-item-text { - display: block; - padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); - color: var(--bs-dropdown-link-color); + display: block; + padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); + color: var(--bs-dropdown-link-color); } .dropdown-menu-dark { - --bs-dropdown-color: #dee2e6; - --bs-dropdown-bg: #343a40; - --bs-dropdown-border-color: var(--bs-border-color-translucent); - --bs-dropdown-box-shadow: ; - --bs-dropdown-link-color: #dee2e6; - --bs-dropdown-link-hover-color: #fff; - --bs-dropdown-divider-bg: var(--bs-border-color-translucent); - --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15); - --bs-dropdown-link-active-color: #fff; - --bs-dropdown-link-active-bg: #0d6efd; - --bs-dropdown-link-disabled-color: #adb5bd; - --bs-dropdown-header-color: #adb5bd; + --bs-dropdown-color: #dee2e6; + --bs-dropdown-bg: #343a40; + --bs-dropdown-border-color: var(--bs-border-color-translucent); + --bs-dropdown-box-shadow: ; + --bs-dropdown-link-color: #dee2e6; + --bs-dropdown-link-hover-color: #fff; + --bs-dropdown-divider-bg: var(--bs-border-color-translucent); + --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15); + --bs-dropdown-link-active-color: #fff; + --bs-dropdown-link-active-bg: #0d6efd; + --bs-dropdown-link-disabled-color: #adb5bd; + --bs-dropdown-header-color: #adb5bd; } .btn-group, .btn-group-vertical { - position: relative; - display: inline-flex; - vertical-align: middle; + position: relative; + display: inline-flex; + vertical-align: middle; } + .btn-group > .btn, .btn-group-vertical > .btn { - position: relative; - flex: 1 1 auto; + position: relative; + flex: 1 1 auto; } + .btn-group > .btn-check:checked + .btn, .btn-group > .btn-check:focus + .btn, .btn-group > .btn:hover, @@ -3738,246 +4160,271 @@ textarea.form-control-lg { .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn.active { - z-index: 1; + z-index: 1; } .btn-toolbar { - display: flex; - flex-wrap: wrap; - justify-content: flex-start; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } + .btn-toolbar .input-group { - width: auto; + width: auto; } .btn-group { - border-radius: var(--bs-border-radius); + border-radius: var(--bs-border-radius); } + .btn-group > :not(.btn-check:first-child) + .btn, .btn-group > .btn-group:not(:first-child) { - margin-left: calc(var(--bs-border-width) * -1); + margin-left: calc(var(--bs-border-width) * -1); } + .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn.dropdown-toggle-split:first-child, .btn-group > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .btn-group > .btn:nth-child(n+3), .btn-group > :not(.btn-check) + .btn, .btn-group > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; } .dropdown-toggle-split { - padding-right: 0.5625rem; - padding-left: 0.5625rem; + padding-right: 0.5625rem; + padding-left: 0.5625rem; } + .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { - margin-left: 0; + margin-left: 0; } + .dropstart .dropdown-toggle-split::before { - margin-right: 0; + margin-right: 0; } .btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { - padding-right: 0.375rem; - padding-left: 0.375rem; + padding-right: 0.375rem; + padding-left: 0.375rem; } .btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { - padding-right: 0.75rem; - padding-left: 0.75rem; + padding-right: 0.75rem; + padding-left: 0.75rem; } .btn-group-vertical { - flex-direction: column; - align-items: flex-start; - justify-content: center; + flex-direction: column; + align-items: flex-start; + justify-content: center; } + .btn-group-vertical > .btn, .btn-group-vertical > .btn-group { - width: 100%; + width: 100%; } + .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) { - margin-top: calc(var(--bs-border-width) * -1); + margin-top: calc(var(--bs-border-width) * -1); } + .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; } + .btn-group-vertical > .btn ~ .btn, .btn-group-vertical > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-top-right-radius: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; } .nav { - --bs-nav-link-padding-x: 1rem; - --bs-nav-link-padding-y: 0.5rem; - --bs-nav-link-font-weight: ; - --bs-nav-link-color: var(--bs-link-color); - --bs-nav-link-hover-color: var(--bs-link-hover-color); - --bs-nav-link-disabled-color: var(--bs-secondary-color); - display: flex; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none; + --bs-nav-link-padding-x: 1rem; + --bs-nav-link-padding-y: 0.5rem; + --bs-nav-link-font-weight: ; + --bs-nav-link-color: var(--bs-link-color); + --bs-nav-link-hover-color: var(--bs-link-hover-color); + --bs-nav-link-disabled-color: var(--bs-secondary-color); + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; } .nav-link { - display: block; - padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); - font-size: var(--bs-nav-link-font-size); - font-weight: var(--bs-nav-link-font-weight); - color: var(--bs-nav-link-color); - text-decoration: none; - background: none; - border: 0; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; + display: block; + padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); + font-size: var(--bs-nav-link-font-size); + font-weight: var(--bs-nav-link-font-weight); + color: var(--bs-nav-link-color); + text-decoration: none; + background: none; + border: 0; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .nav-link { - transition: none; - } + .nav-link { + transition: none; + } } + .nav-link:hover, .nav-link:focus { - color: var(--bs-nav-link-hover-color); + color: var(--bs-nav-link-hover-color); } + .nav-link:focus-visible { - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .nav-link.disabled, .nav-link:disabled { - color: var(--bs-nav-link-disabled-color); - pointer-events: none; - cursor: default; + color: var(--bs-nav-link-disabled-color); + pointer-events: none; + cursor: default; } .nav-tabs { - --bs-nav-tabs-border-width: var(--bs-border-width); - --bs-nav-tabs-border-color: var(--bs-border-color); - --bs-nav-tabs-border-radius: var(--bs-border-radius); - --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color); - --bs-nav-tabs-link-active-color: var(--bs-emphasis-color); - --bs-nav-tabs-link-active-bg: var(--bs-body-bg); - --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg); - border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); + --bs-nav-tabs-border-width: var(--bs-border-width); + --bs-nav-tabs-border-color: var(--bs-border-color); + --bs-nav-tabs-border-radius: var(--bs-border-radius); + --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color); + --bs-nav-tabs-link-active-color: var(--bs-emphasis-color); + --bs-nav-tabs-link-active-bg: var(--bs-body-bg); + --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg); + border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); } + .nav-tabs .nav-link { - margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); - border: var(--bs-nav-tabs-border-width) solid transparent; - border-top-left-radius: var(--bs-nav-tabs-border-radius); - border-top-right-radius: var(--bs-nav-tabs-border-radius); + margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); + border: var(--bs-nav-tabs-border-width) solid transparent; + border-top-left-radius: var(--bs-nav-tabs-border-radius); + border-top-right-radius: var(--bs-nav-tabs-border-radius); } + .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - isolation: isolate; - border-color: var(--bs-nav-tabs-link-hover-border-color); + isolation: isolate; + border-color: var(--bs-nav-tabs-link-hover-border-color); } + .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { - color: var(--bs-nav-tabs-link-active-color); - background-color: var(--bs-nav-tabs-link-active-bg); - border-color: var(--bs-nav-tabs-link-active-border-color); + color: var(--bs-nav-tabs-link-active-color); + background-color: var(--bs-nav-tabs-link-active-bg); + border-color: var(--bs-nav-tabs-link-active-border-color); } + .nav-tabs .dropdown-menu { - margin-top: calc(-1 * var(--bs-nav-tabs-border-width)); - border-top-left-radius: 0; - border-top-right-radius: 0; + margin-top: calc(-1 * var(--bs-nav-tabs-border-width)); + border-top-left-radius: 0; + border-top-right-radius: 0; } .nav-pills { - --bs-nav-pills-border-radius: var(--bs-border-radius); - --bs-nav-pills-link-active-color: #fff; - --bs-nav-pills-link-active-bg: #0d6efd; + --bs-nav-pills-border-radius: var(--bs-border-radius); + --bs-nav-pills-link-active-color: #fff; + --bs-nav-pills-link-active-bg: #0d6efd; } + .nav-pills .nav-link { - border-radius: var(--bs-nav-pills-border-radius); + border-radius: var(--bs-nav-pills-border-radius); } + .nav-pills .nav-link.active, .nav-pills .show > .nav-link { - color: var(--bs-nav-pills-link-active-color); - background-color: var(--bs-nav-pills-link-active-bg); + color: var(--bs-nav-pills-link-active-color); + background-color: var(--bs-nav-pills-link-active-bg); } .nav-underline { - --bs-nav-underline-gap: 1rem; - --bs-nav-underline-border-width: 0.125rem; - --bs-nav-underline-link-active-color: var(--bs-emphasis-color); - gap: var(--bs-nav-underline-gap); + --bs-nav-underline-gap: 1rem; + --bs-nav-underline-border-width: 0.125rem; + --bs-nav-underline-link-active-color: var(--bs-emphasis-color); + gap: var(--bs-nav-underline-gap); } + .nav-underline .nav-link { - padding-right: 0; - padding-left: 0; - border-bottom: var(--bs-nav-underline-border-width) solid transparent; + padding-right: 0; + padding-left: 0; + border-bottom: var(--bs-nav-underline-border-width) solid transparent; } + .nav-underline .nav-link:hover, .nav-underline .nav-link:focus { - border-bottom-color: currentcolor; + border-bottom-color: currentcolor; } + .nav-underline .nav-link.active, .nav-underline .show > .nav-link { - font-weight: 700; - color: var(--bs-nav-underline-link-active-color); - border-bottom-color: currentcolor; + font-weight: 700; + color: var(--bs-nav-underline-link-active-color); + border-bottom-color: currentcolor; } .nav-fill > .nav-link, .nav-fill .nav-item { - flex: 1 1 auto; - text-align: center; + flex: 1 1 auto; + text-align: center; } .nav-justified > .nav-link, .nav-justified .nav-item { - flex-basis: 0; - flex-grow: 1; - text-align: center; + flex-basis: 0; + flex-grow: 1; + text-align: center; } .nav-fill .nav-item .nav-link, .nav-justified .nav-item .nav-link { - width: 100%; + width: 100%; } .tab-content > .tab-pane { - display: none; + display: none; } + .tab-content > .active { - display: block; + display: block; } .navbar { - --bs-navbar-padding-x: 0; - --bs-navbar-padding-y: 0.5rem; - --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65); - --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8); - --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3); - --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1); - --bs-navbar-brand-padding-y: 0.3125rem; - --bs-navbar-brand-margin-end: 1rem; - --bs-navbar-brand-font-size: 1.25rem; - --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1); - --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1); - --bs-navbar-nav-link-padding-x: 0.5rem; - --bs-navbar-toggler-padding-y: 0.25rem; - --bs-navbar-toggler-padding-x: 0.75rem; - --bs-navbar-toggler-font-size: 1.25rem; - --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); - --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15); - --bs-navbar-toggler-border-radius: var(--bs-border-radius); - --bs-navbar-toggler-focus-width: 0.25rem; - --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; - position: relative; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x); + --bs-navbar-padding-x: 0; + --bs-navbar-padding-y: 0.5rem; + --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65); + --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8); + --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3); + --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-brand-padding-y: 0.3125rem; + --bs-navbar-brand-margin-end: 1rem; + --bs-navbar-brand-font-size: 1.25rem; + --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-nav-link-padding-x: 0.5rem; + --bs-navbar-toggler-padding-y: 0.25rem; + --bs-navbar-toggler-padding-x: 0.75rem; + --bs-navbar-toggler-font-size: 1.25rem; + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15); + --bs-navbar-toggler-border-radius: var(--bs-border-radius); + --bs-navbar-toggler-focus-width: 0.25rem; + --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x); } + .navbar > .container, .navbar > .container-fluid, .navbar > .container-sm, @@ -3985,315 +4432,426 @@ textarea.form-control-lg { .navbar > .container-lg, .navbar > .container-xl, .navbar > .container-xxl { - display: flex; - flex-wrap: inherit; - align-items: center; - justify-content: space-between; + display: flex; + flex-wrap: inherit; + align-items: center; + justify-content: space-between; } + .navbar-brand { - padding-top: var(--bs-navbar-brand-padding-y); - padding-bottom: var(--bs-navbar-brand-padding-y); - margin-right: var(--bs-navbar-brand-margin-end); - font-size: var(--bs-navbar-brand-font-size); - color: var(--bs-navbar-brand-color); - text-decoration: none; - white-space: nowrap; + padding-top: var(--bs-navbar-brand-padding-y); + padding-bottom: var(--bs-navbar-brand-padding-y); + margin-right: var(--bs-navbar-brand-margin-end); + font-size: var(--bs-navbar-brand-font-size); + color: var(--bs-navbar-brand-color); + text-decoration: none; + white-space: nowrap; } + .navbar-brand:hover, .navbar-brand:focus { - color: var(--bs-navbar-brand-hover-color); + color: var(--bs-navbar-brand-hover-color); } .navbar-nav { - --bs-nav-link-padding-x: 0; - --bs-nav-link-padding-y: 0.5rem; - --bs-nav-link-font-weight: ; - --bs-nav-link-color: var(--bs-navbar-color); - --bs-nav-link-hover-color: var(--bs-navbar-hover-color); - --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color); - display: flex; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none; + --bs-nav-link-padding-x: 0; + --bs-nav-link-padding-y: 0.5rem; + --bs-nav-link-font-weight: ; + --bs-nav-link-color: var(--bs-navbar-color); + --bs-nav-link-hover-color: var(--bs-navbar-hover-color); + --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color); + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; } + .navbar-nav .nav-link.active, .navbar-nav .nav-link.show { - color: var(--bs-navbar-active-color); + color: var(--bs-navbar-active-color); } + .navbar-nav .dropdown-menu { - position: static; + position: static; } .navbar-text { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: var(--bs-navbar-color); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-navbar-color); } + .navbar-text a, .navbar-text a:hover, .navbar-text a:focus { - color: var(--bs-navbar-active-color); + color: var(--bs-navbar-active-color); } .navbar-collapse { - flex-basis: 100%; - flex-grow: 1; - align-items: center; + flex-basis: 100%; + flex-grow: 1; + align-items: center; } .navbar-toggler { - padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x); - font-size: var(--bs-navbar-toggler-font-size); - line-height: 1; - color: var(--bs-navbar-color); - background-color: transparent; - border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color); - border-radius: var(--bs-navbar-toggler-border-radius); - transition: var(--bs-navbar-toggler-transition); + padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x); + font-size: var(--bs-navbar-toggler-font-size); + line-height: 1; + color: var(--bs-navbar-color); + background-color: transparent; + border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color); + border-radius: var(--bs-navbar-toggler-border-radius); + transition: var(--bs-navbar-toggler-transition); } + @media (prefers-reduced-motion: reduce) { - .navbar-toggler { - transition: none; - } + .navbar-toggler { + transition: none; + } } + .navbar-toggler:hover { - text-decoration: none; + text-decoration: none; } + .navbar-toggler:focus { - text-decoration: none; - outline: 0; - box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width); + text-decoration: none; + outline: 0; + box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width); } .navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - background-image: var(--bs-navbar-toggler-icon-bg); - background-repeat: no-repeat; - background-position: center; - background-size: 100%; + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + background-image: var(--bs-navbar-toggler-icon-bg); + background-repeat: no-repeat; + background-position: center; + background-size: 100%; } .navbar-nav-scroll { - max-height: var(--bs-scroll-height, 75vh); - overflow-y: auto; + max-height: var(--bs-scroll-height, 75vh); + overflow-y: auto; } @media (min-width: 576px) { - .navbar-expand-sm { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-sm .navbar-nav { - flex-direction: row; - } - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-sm .navbar-nav .nav-link { - padding-right: var(--bs-navbar-nav-link-padding-x); - padding-left: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-sm .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-sm .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-sm .navbar-toggler { - display: none; - } - .navbar-expand-sm .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-sm .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-sm .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-sm { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-sm .navbar-nav { + flex-direction: row; + } + + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-sm .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-sm .navbar-toggler { + display: none; + } + + .navbar-expand-sm .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-sm .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-sm .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 768px) { - .navbar-expand-md { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-md .navbar-nav { - flex-direction: row; - } - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-md .navbar-nav .nav-link { - padding-right: var(--bs-navbar-nav-link-padding-x); - padding-left: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-md .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-md .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-md .navbar-toggler { - display: none; - } - .navbar-expand-md .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-md .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-md .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-md { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-md .navbar-nav { + flex-direction: row; + } + + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-md .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-md .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-md .navbar-toggler { + display: none; + } + + .navbar-expand-md .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-md .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-md .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 992px) { - .navbar-expand-lg { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { - flex-direction: row; - } - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-lg .navbar-nav .nav-link { - padding-right: var(--bs-navbar-nav-link-padding-x); - padding-left: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-lg .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-lg .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; - } - .navbar-expand-lg .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-lg .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-lg .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-lg { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-lg .navbar-nav { + flex-direction: row; + } + + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-lg .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-lg .navbar-toggler { + display: none; + } + + .navbar-expand-lg .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-lg .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-lg .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 1200px) { - .navbar-expand-xl { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-xl .navbar-nav { - flex-direction: row; - } - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xl .navbar-nav .nav-link { - padding-right: var(--bs-navbar-nav-link-padding-x); - padding-left: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-xl .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-xl .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-xl .navbar-toggler { - display: none; - } - .navbar-expand-xl .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-xl .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-xl .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-xl { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-xl .navbar-nav { + flex-direction: row; + } + + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-xl .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-xl .navbar-toggler { + display: none; + } + + .navbar-expand-xl .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-xl .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-xl .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 1400px) { - .navbar-expand-xxl { + .navbar-expand-xxl { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-xxl .navbar-nav { + flex-direction: row; + } + + .navbar-expand-xxl .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-xxl .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-xxl .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-xxl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-xxl .navbar-toggler { + display: none; + } + + .navbar-expand-xxl .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-xxl .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-xxl .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } +} + +.navbar-expand { flex-wrap: nowrap; justify-content: flex-start; - } - .navbar-expand-xxl .navbar-nav { +} + +.navbar-expand .navbar-nav { flex-direction: row; - } - .navbar-expand-xxl .navbar-nav .dropdown-menu { +} + +.navbar-expand .navbar-nav .dropdown-menu { position: absolute; - } - .navbar-expand-xxl .navbar-nav .nav-link { +} + +.navbar-expand .navbar-nav .nav-link { padding-right: var(--bs-navbar-nav-link-padding-x); padding-left: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-xxl .navbar-nav-scroll { +} + +.navbar-expand .navbar-nav-scroll { overflow: visible; - } - .navbar-expand-xxl .navbar-collapse { +} + +.navbar-expand .navbar-collapse { display: flex !important; flex-basis: auto; - } - .navbar-expand-xxl .navbar-toggler { +} + +.navbar-expand .navbar-toggler { display: none; - } - .navbar-expand-xxl .offcanvas { +} + +.navbar-expand .offcanvas { position: static; z-index: auto; flex-grow: 1; @@ -4304,2007 +4862,2581 @@ textarea.form-control-lg { border: 0 !important; transform: none !important; transition: none; - } - .navbar-expand-xxl .offcanvas .offcanvas-header { +} + +.navbar-expand .offcanvas .offcanvas-header { display: none; - } - .navbar-expand-xxl .offcanvas .offcanvas-body { +} + +.navbar-expand .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; - } -} -.navbar-expand { - flex-wrap: nowrap; - justify-content: flex-start; -} -.navbar-expand .navbar-nav { - flex-direction: row; -} -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute; -} -.navbar-expand .navbar-nav .nav-link { - padding-right: var(--bs-navbar-nav-link-padding-x); - padding-left: var(--bs-navbar-nav-link-padding-x); -} -.navbar-expand .navbar-nav-scroll { - overflow: visible; -} -.navbar-expand .navbar-collapse { - display: flex !important; - flex-basis: auto; -} -.navbar-expand .navbar-toggler { - display: none; -} -.navbar-expand .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; -} -.navbar-expand .offcanvas .offcanvas-header { - display: none; -} -.navbar-expand .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; } .navbar-dark, .navbar[data-bs-theme=dark] { - --bs-navbar-color: rgba(255, 255, 255, 0.55); - --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); - --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); - --bs-navbar-active-color: #fff; - --bs-navbar-brand-color: #fff; - --bs-navbar-brand-hover-color: #fff; - --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1); - --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-color: rgba(255, 255, 255, 0.55); + --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); + --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); + --bs-navbar-active-color: #fff; + --bs-navbar-brand-color: #fff; + --bs-navbar-brand-hover-color: #fff; + --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1); + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } [data-bs-theme=dark] .navbar-toggler-icon { - --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .card { - --bs-card-spacer-y: 1rem; - --bs-card-spacer-x: 1rem; - --bs-card-title-spacer-y: 0.5rem; - --bs-card-title-color: ; - --bs-card-subtitle-color: ; - --bs-card-border-width: var(--bs-border-width); - --bs-card-border-color: var(--bs-border-color-translucent); - --bs-card-border-radius: var(--bs-border-radius); - --bs-card-box-shadow: ; - --bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); - --bs-card-cap-padding-y: 0.5rem; - --bs-card-cap-padding-x: 1rem; - --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03); - --bs-card-cap-color: ; - --bs-card-height: ; - --bs-card-color: ; - --bs-card-bg: var(--bs-body-bg); - --bs-card-img-overlay-padding: 1rem; - --bs-card-group-margin: 0.75rem; - position: relative; - display: flex; - flex-direction: column; - min-width: 0; - height: var(--bs-card-height); - color: var(--bs-body-color); - word-wrap: break-word; - background-color: var(--bs-card-bg); - background-clip: border-box; - border: var(--bs-card-border-width) solid var(--bs-card-border-color); - border-radius: var(--bs-card-border-radius); + --bs-card-spacer-y: 1rem; + --bs-card-spacer-x: 1rem; + --bs-card-title-spacer-y: 0.5rem; + --bs-card-title-color: ; + --bs-card-subtitle-color: ; + --bs-card-border-width: var(--bs-border-width); + --bs-card-border-color: var(--bs-border-color-translucent); + --bs-card-border-radius: var(--bs-border-radius); + --bs-card-box-shadow: ; + --bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); + --bs-card-cap-padding-y: 0.5rem; + --bs-card-cap-padding-x: 1rem; + --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03); + --bs-card-cap-color: ; + --bs-card-height: ; + --bs-card-color: ; + --bs-card-bg: var(--bs-body-bg); + --bs-card-img-overlay-padding: 1rem; + --bs-card-group-margin: 0.75rem; + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + height: var(--bs-card-height); + color: var(--bs-body-color); + word-wrap: break-word; + background-color: var(--bs-card-bg); + background-clip: border-box; + border: var(--bs-card-border-width) solid var(--bs-card-border-color); + border-radius: var(--bs-card-border-radius); } + .card > hr { - margin-right: 0; - margin-left: 0; + margin-right: 0; + margin-left: 0; } + .card > .list-group { - border-top: inherit; - border-bottom: inherit; + border-top: inherit; + border-bottom: inherit; } + .card > .list-group:first-child { - border-top-width: 0; - border-top-left-radius: var(--bs-card-inner-border-radius); - border-top-right-radius: var(--bs-card-inner-border-radius); + border-top-width: 0; + border-top-left-radius: var(--bs-card-inner-border-radius); + border-top-right-radius: var(--bs-card-inner-border-radius); } + .card > .list-group:last-child { - border-bottom-width: 0; - border-bottom-right-radius: var(--bs-card-inner-border-radius); - border-bottom-left-radius: var(--bs-card-inner-border-radius); + border-bottom-width: 0; + border-bottom-right-radius: var(--bs-card-inner-border-radius); + border-bottom-left-radius: var(--bs-card-inner-border-radius); } + .card > .card-header + .list-group, .card > .list-group + .card-footer { - border-top: 0; + border-top: 0; } .card-body { - flex: 1 1 auto; - padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); - color: var(--bs-card-color); + flex: 1 1 auto; + padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); + color: var(--bs-card-color); } .card-title { - margin-bottom: var(--bs-card-title-spacer-y); - color: var(--bs-card-title-color); + margin-bottom: var(--bs-card-title-spacer-y); + color: var(--bs-card-title-color); } .card-subtitle { - margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); - margin-bottom: 0; - color: var(--bs-card-subtitle-color); + margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); + margin-bottom: 0; + color: var(--bs-card-subtitle-color); } .card-text:last-child { - margin-bottom: 0; + margin-bottom: 0; } .card-link + .card-link { - margin-left: var(--bs-card-spacer-x); + margin-left: var(--bs-card-spacer-x); } .card-header { - padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); - margin-bottom: 0; - color: var(--bs-card-cap-color); - background-color: var(--bs-card-cap-bg); - border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color); + padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); + margin-bottom: 0; + color: var(--bs-card-cap-color); + background-color: var(--bs-card-cap-bg); + border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color); } + .card-header:first-child { - border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0; + border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0; } .card-footer { - padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); - color: var(--bs-card-cap-color); - background-color: var(--bs-card-cap-bg); - border-top: var(--bs-card-border-width) solid var(--bs-card-border-color); + padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); + color: var(--bs-card-cap-color); + background-color: var(--bs-card-cap-bg); + border-top: var(--bs-card-border-width) solid var(--bs-card-border-color); } + .card-footer:last-child { - border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius); + border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius); } .card-header-tabs { - margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); - margin-bottom: calc(-1 * var(--bs-card-cap-padding-y)); - margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); - border-bottom: 0; + margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-bottom: calc(-1 * var(--bs-card-cap-padding-y)); + margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); + border-bottom: 0; } + .card-header-tabs .nav-link.active { - background-color: var(--bs-card-bg); - border-bottom-color: var(--bs-card-bg); + background-color: var(--bs-card-bg); + border-bottom-color: var(--bs-card-bg); } .card-header-pills { - margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); - margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); } .card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: var(--bs-card-img-overlay-padding); - border-radius: var(--bs-card-inner-border-radius); + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: var(--bs-card-img-overlay-padding); + border-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-top, .card-img-bottom { - width: 100%; + width: 100%; } .card-img, .card-img-top { - border-top-left-radius: var(--bs-card-inner-border-radius); - border-top-right-radius: var(--bs-card-inner-border-radius); + border-top-left-radius: var(--bs-card-inner-border-radius); + border-top-right-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-bottom { - border-bottom-right-radius: var(--bs-card-inner-border-radius); - border-bottom-left-radius: var(--bs-card-inner-border-radius); + border-bottom-right-radius: var(--bs-card-inner-border-radius); + border-bottom-left-radius: var(--bs-card-inner-border-radius); } .card-group > .card { - margin-bottom: var(--bs-card-group-margin); + margin-bottom: var(--bs-card-group-margin); } + @media (min-width: 576px) { - .card-group { - display: flex; - flex-flow: row wrap; - } - .card-group > .card { - flex: 1 0 0%; - margin-bottom: 0; - } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; - } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; - } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; - } + .card-group { + display: flex; + flex-flow: row wrap; + } + + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; + } + + .card-group > .card + .card { + margin-left: 0; + border-left: 0; + } + + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; + } + + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; + } + + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; + } + + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; + } } .accordion { - --bs-accordion-color: var(--bs-body-color); - --bs-accordion-bg: var(--bs-body-bg); - --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; - --bs-accordion-border-color: var(--bs-border-color); - --bs-accordion-border-width: var(--bs-border-width); - --bs-accordion-border-radius: var(--bs-border-radius); - --bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); - --bs-accordion-btn-padding-x: 1.25rem; - --bs-accordion-btn-padding-y: 1rem; - --bs-accordion-btn-color: var(--bs-body-color); - --bs-accordion-btn-bg: var(--bs-accordion-bg); - --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); - --bs-accordion-btn-icon-width: 1.25rem; - --bs-accordion-btn-icon-transform: rotate(-180deg); - --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; - --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); - --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); - --bs-accordion-body-padding-x: 1.25rem; - --bs-accordion-body-padding-y: 1rem; - --bs-accordion-active-color: var(--bs-primary-text-emphasis); - --bs-accordion-active-bg: var(--bs-primary-bg-subtle); + --bs-accordion-color: var(--bs-body-color); + --bs-accordion-bg: var(--bs-body-bg); + --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; + --bs-accordion-border-color: var(--bs-border-color); + --bs-accordion-border-width: var(--bs-border-width); + --bs-accordion-border-radius: var(--bs-border-radius); + --bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); + --bs-accordion-btn-padding-x: 1.25rem; + --bs-accordion-btn-padding-y: 1rem; + --bs-accordion-btn-color: var(--bs-body-color); + --bs-accordion-btn-bg: var(--bs-accordion-bg); + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-icon-width: 1.25rem; + --bs-accordion-btn-icon-transform: rotate(-180deg); + --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + --bs-accordion-body-padding-x: 1.25rem; + --bs-accordion-body-padding-y: 1rem; + --bs-accordion-active-color: var(--bs-primary-text-emphasis); + --bs-accordion-active-bg: var(--bs-primary-bg-subtle); } .accordion-button { - position: relative; - display: flex; - align-items: center; - width: 100%; - padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x); - font-size: 1rem; - color: var(--bs-accordion-btn-color); - text-align: left; - background-color: var(--bs-accordion-btn-bg); - border: 0; - border-radius: 0; - overflow-anchor: none; - transition: var(--bs-accordion-transition); + position: relative; + display: flex; + align-items: center; + width: 100%; + padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x); + font-size: 1rem; + color: var(--bs-accordion-btn-color); + text-align: left; + background-color: var(--bs-accordion-btn-bg); + border: 0; + border-radius: 0; + overflow-anchor: none; + transition: var(--bs-accordion-transition); } + @media (prefers-reduced-motion: reduce) { - .accordion-button { - transition: none; - } + .accordion-button { + transition: none; + } } + .accordion-button:not(.collapsed) { - color: var(--bs-accordion-active-color); - background-color: var(--bs-accordion-active-bg); - box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color); + color: var(--bs-accordion-active-color); + background-color: var(--bs-accordion-active-bg); + box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color); } + .accordion-button:not(.collapsed)::after { - background-image: var(--bs-accordion-btn-active-icon); - transform: var(--bs-accordion-btn-icon-transform); + background-image: var(--bs-accordion-btn-active-icon); + transform: var(--bs-accordion-btn-icon-transform); } + .accordion-button::after { - flex-shrink: 0; - width: var(--bs-accordion-btn-icon-width); - height: var(--bs-accordion-btn-icon-width); - margin-left: auto; - content: ""; - background-image: var(--bs-accordion-btn-icon); - background-repeat: no-repeat; - background-size: var(--bs-accordion-btn-icon-width); - transition: var(--bs-accordion-btn-icon-transition); + flex-shrink: 0; + width: var(--bs-accordion-btn-icon-width); + height: var(--bs-accordion-btn-icon-width); + margin-left: auto; + content: ""; + background-image: var(--bs-accordion-btn-icon); + background-repeat: no-repeat; + background-size: var(--bs-accordion-btn-icon-width); + transition: var(--bs-accordion-btn-icon-transition); } + @media (prefers-reduced-motion: reduce) { - .accordion-button::after { - transition: none; - } + .accordion-button::after { + transition: none; + } } + .accordion-button:hover { - z-index: 2; + z-index: 2; } + .accordion-button:focus { - z-index: 3; - outline: 0; - box-shadow: var(--bs-accordion-btn-focus-box-shadow); + z-index: 3; + outline: 0; + box-shadow: var(--bs-accordion-btn-focus-box-shadow); } .accordion-header { - margin-bottom: 0; + margin-bottom: 0; } .accordion-item { - color: var(--bs-accordion-color); - background-color: var(--bs-accordion-bg); - border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color); + color: var(--bs-accordion-color); + background-color: var(--bs-accordion-bg); + border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color); } + .accordion-item:first-of-type { - border-top-left-radius: var(--bs-accordion-border-radius); - border-top-right-radius: var(--bs-accordion-border-radius); + border-top-left-radius: var(--bs-accordion-border-radius); + border-top-right-radius: var(--bs-accordion-border-radius); } + .accordion-item:first-of-type > .accordion-header .accordion-button { - border-top-left-radius: var(--bs-accordion-inner-border-radius); - border-top-right-radius: var(--bs-accordion-inner-border-radius); + border-top-left-radius: var(--bs-accordion-inner-border-radius); + border-top-right-radius: var(--bs-accordion-inner-border-radius); } + .accordion-item:not(:first-of-type) { - border-top: 0; + border-top: 0; } + .accordion-item:last-of-type { - border-bottom-right-radius: var(--bs-accordion-border-radius); - border-bottom-left-radius: var(--bs-accordion-border-radius); + border-bottom-right-radius: var(--bs-accordion-border-radius); + border-bottom-left-radius: var(--bs-accordion-border-radius); } + .accordion-item:last-of-type > .accordion-header .accordion-button.collapsed { - border-bottom-right-radius: var(--bs-accordion-inner-border-radius); - border-bottom-left-radius: var(--bs-accordion-inner-border-radius); + border-bottom-right-radius: var(--bs-accordion-inner-border-radius); + border-bottom-left-radius: var(--bs-accordion-inner-border-radius); } + .accordion-item:last-of-type > .accordion-collapse { - border-bottom-right-radius: var(--bs-accordion-border-radius); - border-bottom-left-radius: var(--bs-accordion-border-radius); + border-bottom-right-radius: var(--bs-accordion-border-radius); + border-bottom-left-radius: var(--bs-accordion-border-radius); } .accordion-body { - padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); + padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); } .accordion-flush > .accordion-item { - border-right: 0; - border-left: 0; - border-radius: 0; + border-right: 0; + border-left: 0; + border-radius: 0; } + .accordion-flush > .accordion-item:first-child { - border-top: 0; + border-top: 0; } + .accordion-flush > .accordion-item:last-child { - border-bottom: 0; + border-bottom: 0; } + .accordion-flush > .accordion-item > .accordion-header .accordion-button, .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed { - border-radius: 0; + border-radius: 0; } + .accordion-flush > .accordion-item > .accordion-collapse { - border-radius: 0; + border-radius: 0; } [data-bs-theme=dark] .accordion-button::after { - --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); - --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } .breadcrumb { - --bs-breadcrumb-padding-x: 0; - --bs-breadcrumb-padding-y: 0; - --bs-breadcrumb-margin-bottom: 1rem; - --bs-breadcrumb-bg: ; - --bs-breadcrumb-border-radius: ; - --bs-breadcrumb-divider-color: var(--bs-secondary-color); - --bs-breadcrumb-item-padding-x: 0.5rem; - --bs-breadcrumb-item-active-color: var(--bs-secondary-color); - display: flex; - flex-wrap: wrap; - padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); - margin-bottom: var(--bs-breadcrumb-margin-bottom); - font-size: var(--bs-breadcrumb-font-size); - list-style: none; - background-color: var(--bs-breadcrumb-bg); - border-radius: var(--bs-breadcrumb-border-radius); + --bs-breadcrumb-padding-x: 0; + --bs-breadcrumb-padding-y: 0; + --bs-breadcrumb-margin-bottom: 1rem; + --bs-breadcrumb-bg: ; + --bs-breadcrumb-border-radius: ; + --bs-breadcrumb-divider-color: var(--bs-secondary-color); + --bs-breadcrumb-item-padding-x: 0.5rem; + --bs-breadcrumb-item-active-color: var(--bs-secondary-color); + display: flex; + flex-wrap: wrap; + padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); + margin-bottom: var(--bs-breadcrumb-margin-bottom); + font-size: var(--bs-breadcrumb-font-size); + list-style: none; + background-color: var(--bs-breadcrumb-bg); + border-radius: var(--bs-breadcrumb-border-radius); } .breadcrumb-item + .breadcrumb-item { - padding-left: var(--bs-breadcrumb-item-padding-x); + padding-left: var(--bs-breadcrumb-item-padding-x); } + .breadcrumb-item + .breadcrumb-item::before { - float: left; - padding-right: var(--bs-breadcrumb-item-padding-x); - color: var(--bs-breadcrumb-divider-color); - content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; + float: left; + padding-right: var(--bs-breadcrumb-item-padding-x); + color: var(--bs-breadcrumb-divider-color); + content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; } + .breadcrumb-item.active { - color: var(--bs-breadcrumb-item-active-color); + color: var(--bs-breadcrumb-item-active-color); } .pagination { - --bs-pagination-padding-x: 0.75rem; - --bs-pagination-padding-y: 0.375rem; - --bs-pagination-font-size: 1rem; - --bs-pagination-color: var(--bs-link-color); - --bs-pagination-bg: var(--bs-body-bg); - --bs-pagination-border-width: var(--bs-border-width); - --bs-pagination-border-color: var(--bs-border-color); - --bs-pagination-border-radius: var(--bs-border-radius); - --bs-pagination-hover-color: var(--bs-link-hover-color); - --bs-pagination-hover-bg: var(--bs-tertiary-bg); - --bs-pagination-hover-border-color: var(--bs-border-color); - --bs-pagination-focus-color: var(--bs-link-hover-color); - --bs-pagination-focus-bg: var(--bs-secondary-bg); - --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); - --bs-pagination-active-color: #fff; - --bs-pagination-active-bg: #0d6efd; - --bs-pagination-active-border-color: #0d6efd; - --bs-pagination-disabled-color: var(--bs-secondary-color); - --bs-pagination-disabled-bg: var(--bs-secondary-bg); - --bs-pagination-disabled-border-color: var(--bs-border-color); - display: flex; - padding-left: 0; - list-style: none; + --bs-pagination-padding-x: 0.75rem; + --bs-pagination-padding-y: 0.375rem; + --bs-pagination-font-size: 1rem; + --bs-pagination-color: var(--bs-link-color); + --bs-pagination-bg: var(--bs-body-bg); + --bs-pagination-border-width: var(--bs-border-width); + --bs-pagination-border-color: var(--bs-border-color); + --bs-pagination-border-radius: var(--bs-border-radius); + --bs-pagination-hover-color: var(--bs-link-hover-color); + --bs-pagination-hover-bg: var(--bs-tertiary-bg); + --bs-pagination-hover-border-color: var(--bs-border-color); + --bs-pagination-focus-color: var(--bs-link-hover-color); + --bs-pagination-focus-bg: var(--bs-secondary-bg); + --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + --bs-pagination-active-color: #fff; + --bs-pagination-active-bg: #0d6efd; + --bs-pagination-active-border-color: #0d6efd; + --bs-pagination-disabled-color: var(--bs-secondary-color); + --bs-pagination-disabled-bg: var(--bs-secondary-bg); + --bs-pagination-disabled-border-color: var(--bs-border-color); + display: flex; + padding-left: 0; + list-style: none; } .page-link { - position: relative; - display: block; - padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x); - font-size: var(--bs-pagination-font-size); - color: var(--bs-pagination-color); - text-decoration: none; - background-color: var(--bs-pagination-bg); - border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color); - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + position: relative; + display: block; + padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x); + font-size: var(--bs-pagination-font-size); + color: var(--bs-pagination-color); + text-decoration: none; + background-color: var(--bs-pagination-bg); + border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color); + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .page-link { - transition: none; - } + .page-link { + transition: none; + } } + .page-link:hover { - z-index: 2; - color: var(--bs-pagination-hover-color); - background-color: var(--bs-pagination-hover-bg); - border-color: var(--bs-pagination-hover-border-color); + z-index: 2; + color: var(--bs-pagination-hover-color); + background-color: var(--bs-pagination-hover-bg); + border-color: var(--bs-pagination-hover-border-color); } + .page-link:focus { - z-index: 3; - color: var(--bs-pagination-focus-color); - background-color: var(--bs-pagination-focus-bg); - outline: 0; - box-shadow: var(--bs-pagination-focus-box-shadow); + z-index: 3; + color: var(--bs-pagination-focus-color); + background-color: var(--bs-pagination-focus-bg); + outline: 0; + box-shadow: var(--bs-pagination-focus-box-shadow); } + .page-link.active, .active > .page-link { - z-index: 3; - color: var(--bs-pagination-active-color); - background-color: var(--bs-pagination-active-bg); - border-color: var(--bs-pagination-active-border-color); + z-index: 3; + color: var(--bs-pagination-active-color); + background-color: var(--bs-pagination-active-bg); + border-color: var(--bs-pagination-active-border-color); } + .page-link.disabled, .disabled > .page-link { - color: var(--bs-pagination-disabled-color); - pointer-events: none; - background-color: var(--bs-pagination-disabled-bg); - border-color: var(--bs-pagination-disabled-border-color); + color: var(--bs-pagination-disabled-color); + pointer-events: none; + background-color: var(--bs-pagination-disabled-bg); + border-color: var(--bs-pagination-disabled-border-color); } .page-item:not(:first-child) .page-link { - margin-left: calc(var(--bs-border-width) * -1); + margin-left: calc(var(--bs-border-width) * -1); } + .page-item:first-child .page-link { - border-top-left-radius: var(--bs-pagination-border-radius); - border-bottom-left-radius: var(--bs-pagination-border-radius); + border-top-left-radius: var(--bs-pagination-border-radius); + border-bottom-left-radius: var(--bs-pagination-border-radius); } + .page-item:last-child .page-link { - border-top-right-radius: var(--bs-pagination-border-radius); - border-bottom-right-radius: var(--bs-pagination-border-radius); + border-top-right-radius: var(--bs-pagination-border-radius); + border-bottom-right-radius: var(--bs-pagination-border-radius); } .pagination-lg { - --bs-pagination-padding-x: 1.5rem; - --bs-pagination-padding-y: 0.75rem; - --bs-pagination-font-size: 1.25rem; - --bs-pagination-border-radius: var(--bs-border-radius-lg); + --bs-pagination-padding-x: 1.5rem; + --bs-pagination-padding-y: 0.75rem; + --bs-pagination-font-size: 1.25rem; + --bs-pagination-border-radius: var(--bs-border-radius-lg); } .pagination-sm { - --bs-pagination-padding-x: 0.5rem; - --bs-pagination-padding-y: 0.25rem; - --bs-pagination-font-size: 0.875rem; - --bs-pagination-border-radius: var(--bs-border-radius-sm); + --bs-pagination-padding-x: 0.5rem; + --bs-pagination-padding-y: 0.25rem; + --bs-pagination-font-size: 0.875rem; + --bs-pagination-border-radius: var(--bs-border-radius-sm); } .badge { - --bs-badge-padding-x: 0.65em; - --bs-badge-padding-y: 0.35em; - --bs-badge-font-size: 0.75em; - --bs-badge-font-weight: 700; - --bs-badge-color: #fff; - --bs-badge-border-radius: var(--bs-border-radius); - display: inline-block; - padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); - font-size: var(--bs-badge-font-size); - font-weight: var(--bs-badge-font-weight); - line-height: 1; - color: var(--bs-badge-color); - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: var(--bs-badge-border-radius); + --bs-badge-padding-x: 0.65em; + --bs-badge-padding-y: 0.35em; + --bs-badge-font-size: 0.75em; + --bs-badge-font-weight: 700; + --bs-badge-color: #fff; + --bs-badge-border-radius: var(--bs-border-radius); + display: inline-block; + padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); + font-size: var(--bs-badge-font-size); + font-weight: var(--bs-badge-font-weight); + line-height: 1; + color: var(--bs-badge-color); + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: var(--bs-badge-border-radius); } + .badge:empty { - display: none; + display: none; } .btn .badge { - position: relative; - top: -1px; + position: relative; + top: -1px; } .alert { - --bs-alert-bg: transparent; - --bs-alert-padding-x: 1rem; - --bs-alert-padding-y: 1rem; - --bs-alert-margin-bottom: 1rem; - --bs-alert-color: inherit; - --bs-alert-border-color: transparent; - --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color); - --bs-alert-border-radius: var(--bs-border-radius); - --bs-alert-link-color: inherit; - position: relative; - padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); - margin-bottom: var(--bs-alert-margin-bottom); - color: var(--bs-alert-color); - background-color: var(--bs-alert-bg); - border: var(--bs-alert-border); - border-radius: var(--bs-alert-border-radius); + --bs-alert-bg: transparent; + --bs-alert-padding-x: 1rem; + --bs-alert-padding-y: 1rem; + --bs-alert-margin-bottom: 1rem; + --bs-alert-color: inherit; + --bs-alert-border-color: transparent; + --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color); + --bs-alert-border-radius: var(--bs-border-radius); + --bs-alert-link-color: inherit; + position: relative; + padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); + margin-bottom: var(--bs-alert-margin-bottom); + color: var(--bs-alert-color); + background-color: var(--bs-alert-bg); + border: var(--bs-alert-border); + border-radius: var(--bs-alert-border-radius); } .alert-heading { - color: inherit; + color: inherit; } .alert-link { - font-weight: 700; - color: var(--bs-alert-link-color); + font-weight: 700; + color: var(--bs-alert-link-color); } .alert-dismissible { - padding-right: 3rem; + padding-right: 3rem; } + .alert-dismissible .btn-close { - position: absolute; - top: 0; - right: 0; - z-index: 2; - padding: 1.25rem 1rem; + position: absolute; + top: 0; + right: 0; + z-index: 2; + padding: 1.25rem 1rem; } .alert-primary { - --bs-alert-color: var(--bs-primary-text-emphasis); - --bs-alert-bg: var(--bs-primary-bg-subtle); - --bs-alert-border-color: var(--bs-primary-border-subtle); - --bs-alert-link-color: var(--bs-primary-text-emphasis); + --bs-alert-color: var(--bs-primary-text-emphasis); + --bs-alert-bg: var(--bs-primary-bg-subtle); + --bs-alert-border-color: var(--bs-primary-border-subtle); + --bs-alert-link-color: var(--bs-primary-text-emphasis); } .alert-secondary { - --bs-alert-color: var(--bs-secondary-text-emphasis); - --bs-alert-bg: var(--bs-secondary-bg-subtle); - --bs-alert-border-color: var(--bs-secondary-border-subtle); - --bs-alert-link-color: var(--bs-secondary-text-emphasis); + --bs-alert-color: var(--bs-secondary-text-emphasis); + --bs-alert-bg: var(--bs-secondary-bg-subtle); + --bs-alert-border-color: var(--bs-secondary-border-subtle); + --bs-alert-link-color: var(--bs-secondary-text-emphasis); } .alert-success { - --bs-alert-color: var(--bs-success-text-emphasis); - --bs-alert-bg: var(--bs-success-bg-subtle); - --bs-alert-border-color: var(--bs-success-border-subtle); - --bs-alert-link-color: var(--bs-success-text-emphasis); + --bs-alert-color: var(--bs-success-text-emphasis); + --bs-alert-bg: var(--bs-success-bg-subtle); + --bs-alert-border-color: var(--bs-success-border-subtle); + --bs-alert-link-color: var(--bs-success-text-emphasis); } .alert-info { - --bs-alert-color: var(--bs-info-text-emphasis); - --bs-alert-bg: var(--bs-info-bg-subtle); - --bs-alert-border-color: var(--bs-info-border-subtle); - --bs-alert-link-color: var(--bs-info-text-emphasis); + --bs-alert-color: var(--bs-info-text-emphasis); + --bs-alert-bg: var(--bs-info-bg-subtle); + --bs-alert-border-color: var(--bs-info-border-subtle); + --bs-alert-link-color: var(--bs-info-text-emphasis); } .alert-warning { - --bs-alert-color: var(--bs-warning-text-emphasis); - --bs-alert-bg: var(--bs-warning-bg-subtle); - --bs-alert-border-color: var(--bs-warning-border-subtle); - --bs-alert-link-color: var(--bs-warning-text-emphasis); + --bs-alert-color: var(--bs-warning-text-emphasis); + --bs-alert-bg: var(--bs-warning-bg-subtle); + --bs-alert-border-color: var(--bs-warning-border-subtle); + --bs-alert-link-color: var(--bs-warning-text-emphasis); } .alert-danger { - --bs-alert-color: var(--bs-danger-text-emphasis); - --bs-alert-bg: var(--bs-danger-bg-subtle); - --bs-alert-border-color: var(--bs-danger-border-subtle); - --bs-alert-link-color: var(--bs-danger-text-emphasis); + --bs-alert-color: var(--bs-danger-text-emphasis); + --bs-alert-bg: var(--bs-danger-bg-subtle); + --bs-alert-border-color: var(--bs-danger-border-subtle); + --bs-alert-link-color: var(--bs-danger-text-emphasis); } .alert-light { - --bs-alert-color: var(--bs-light-text-emphasis); - --bs-alert-bg: var(--bs-light-bg-subtle); - --bs-alert-border-color: var(--bs-light-border-subtle); - --bs-alert-link-color: var(--bs-light-text-emphasis); + --bs-alert-color: var(--bs-light-text-emphasis); + --bs-alert-bg: var(--bs-light-bg-subtle); + --bs-alert-border-color: var(--bs-light-border-subtle); + --bs-alert-link-color: var(--bs-light-text-emphasis); } .alert-dark { - --bs-alert-color: var(--bs-dark-text-emphasis); - --bs-alert-bg: var(--bs-dark-bg-subtle); - --bs-alert-border-color: var(--bs-dark-border-subtle); - --bs-alert-link-color: var(--bs-dark-text-emphasis); + --bs-alert-color: var(--bs-dark-text-emphasis); + --bs-alert-bg: var(--bs-dark-bg-subtle); + --bs-alert-border-color: var(--bs-dark-border-subtle); + --bs-alert-link-color: var(--bs-dark-text-emphasis); } @keyframes progress-bar-stripes { - 0% { - background-position-x: 1rem; - } + 0% { + background-position-x: 1rem; + } } + .progress, .progress-stacked { - --bs-progress-height: 1rem; - --bs-progress-font-size: 0.75rem; - --bs-progress-bg: var(--bs-secondary-bg); - --bs-progress-border-radius: var(--bs-border-radius); - --bs-progress-box-shadow: var(--bs-box-shadow-inset); - --bs-progress-bar-color: #fff; - --bs-progress-bar-bg: #0d6efd; - --bs-progress-bar-transition: width 0.6s ease; - display: flex; - height: var(--bs-progress-height); - overflow: hidden; - font-size: var(--bs-progress-font-size); - background-color: var(--bs-progress-bg); - border-radius: var(--bs-progress-border-radius); + --bs-progress-height: 1rem; + --bs-progress-font-size: 0.75rem; + --bs-progress-bg: var(--bs-secondary-bg); + --bs-progress-border-radius: var(--bs-border-radius); + --bs-progress-box-shadow: var(--bs-box-shadow-inset); + --bs-progress-bar-color: #fff; + --bs-progress-bar-bg: #0d6efd; + --bs-progress-bar-transition: width 0.6s ease; + display: flex; + height: var(--bs-progress-height); + overflow: hidden; + font-size: var(--bs-progress-font-size); + background-color: var(--bs-progress-bg); + border-radius: var(--bs-progress-border-radius); } .progress-bar { - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; - color: var(--bs-progress-bar-color); - text-align: center; - white-space: nowrap; - background-color: var(--bs-progress-bar-bg); - transition: var(--bs-progress-bar-transition); + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + color: var(--bs-progress-bar-color); + text-align: center; + white-space: nowrap; + background-color: var(--bs-progress-bar-bg); + transition: var(--bs-progress-bar-transition); } + @media (prefers-reduced-motion: reduce) { - .progress-bar { - transition: none; - } + .progress-bar { + transition: none; + } } .progress-bar-striped { - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: var(--bs-progress-height) var(--bs-progress-height); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: var(--bs-progress-height) var(--bs-progress-height); } .progress-stacked > .progress { - overflow: visible; + overflow: visible; } .progress-stacked > .progress > .progress-bar { - width: 100%; + width: 100%; } .progress-bar-animated { - animation: 1s linear infinite progress-bar-stripes; + animation: 1s linear infinite progress-bar-stripes; } + @media (prefers-reduced-motion: reduce) { - .progress-bar-animated { - animation: none; - } + .progress-bar-animated { + animation: none; + } } .list-group { - --bs-list-group-color: var(--bs-body-color); - --bs-list-group-bg: var(--bs-body-bg); - --bs-list-group-border-color: var(--bs-border-color); - --bs-list-group-border-width: var(--bs-border-width); - --bs-list-group-border-radius: var(--bs-border-radius); - --bs-list-group-item-padding-x: 1rem; - --bs-list-group-item-padding-y: 0.5rem; - --bs-list-group-action-color: var(--bs-secondary-color); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-tertiary-bg); - --bs-list-group-action-active-color: var(--bs-body-color); - --bs-list-group-action-active-bg: var(--bs-secondary-bg); - --bs-list-group-disabled-color: var(--bs-secondary-color); - --bs-list-group-disabled-bg: var(--bs-body-bg); - --bs-list-group-active-color: #fff; - --bs-list-group-active-bg: #0d6efd; - --bs-list-group-active-border-color: #0d6efd; - display: flex; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - border-radius: var(--bs-list-group-border-radius); + --bs-list-group-color: var(--bs-body-color); + --bs-list-group-bg: var(--bs-body-bg); + --bs-list-group-border-color: var(--bs-border-color); + --bs-list-group-border-width: var(--bs-border-width); + --bs-list-group-border-radius: var(--bs-border-radius); + --bs-list-group-item-padding-x: 1rem; + --bs-list-group-item-padding-y: 0.5rem; + --bs-list-group-action-color: var(--bs-secondary-color); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-tertiary-bg); + --bs-list-group-action-active-color: var(--bs-body-color); + --bs-list-group-action-active-bg: var(--bs-secondary-bg); + --bs-list-group-disabled-color: var(--bs-secondary-color); + --bs-list-group-disabled-bg: var(--bs-body-bg); + --bs-list-group-active-color: #fff; + --bs-list-group-active-bg: #0d6efd; + --bs-list-group-active-border-color: #0d6efd; + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + border-radius: var(--bs-list-group-border-radius); } .list-group-numbered { - list-style-type: none; - counter-reset: section; + list-style-type: none; + counter-reset: section; } + .list-group-numbered > .list-group-item::before { - content: counters(section, ".") ". "; - counter-increment: section; + content: counters(section, ".") ". "; + counter-increment: section; } .list-group-item-action { - width: 100%; - color: var(--bs-list-group-action-color); - text-align: inherit; + width: 100%; + color: var(--bs-list-group-action-color); + text-align: inherit; } + .list-group-item-action:hover, .list-group-item-action:focus { - z-index: 1; - color: var(--bs-list-group-action-hover-color); - text-decoration: none; - background-color: var(--bs-list-group-action-hover-bg); + z-index: 1; + color: var(--bs-list-group-action-hover-color); + text-decoration: none; + background-color: var(--bs-list-group-action-hover-bg); } + .list-group-item-action:active { - color: var(--bs-list-group-action-active-color); - background-color: var(--bs-list-group-action-active-bg); + color: var(--bs-list-group-action-active-color); + background-color: var(--bs-list-group-action-active-bg); } .list-group-item { - position: relative; - display: block; - padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x); - color: var(--bs-list-group-color); - text-decoration: none; - background-color: var(--bs-list-group-bg); - border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color); + position: relative; + display: block; + padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x); + color: var(--bs-list-group-color); + text-decoration: none; + background-color: var(--bs-list-group-bg); + border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color); } + .list-group-item:first-child { - border-top-left-radius: inherit; - border-top-right-radius: inherit; + border-top-left-radius: inherit; + border-top-right-radius: inherit; } + .list-group-item:last-child { - border-bottom-right-radius: inherit; - border-bottom-left-radius: inherit; + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; } + .list-group-item.disabled, .list-group-item:disabled { - color: var(--bs-list-group-disabled-color); - pointer-events: none; - background-color: var(--bs-list-group-disabled-bg); + color: var(--bs-list-group-disabled-color); + pointer-events: none; + background-color: var(--bs-list-group-disabled-bg); } + .list-group-item.active { - z-index: 2; - color: var(--bs-list-group-active-color); - background-color: var(--bs-list-group-active-bg); - border-color: var(--bs-list-group-active-border-color); + z-index: 2; + color: var(--bs-list-group-active-color); + background-color: var(--bs-list-group-active-bg); + border-color: var(--bs-list-group-active-border-color); } + .list-group-item + .list-group-item { - border-top-width: 0; + border-top-width: 0; } + .list-group-item + .list-group-item.active { - margin-top: calc(-1 * var(--bs-list-group-border-width)); - border-top-width: var(--bs-list-group-border-width); + margin-top: calc(-1 * var(--bs-list-group-border-width)); + border-top-width: var(--bs-list-group-border-width); } .list-group-horizontal { - flex-direction: row; + flex-direction: row; } + .list-group-horizontal > .list-group-item:first-child:not(:last-child) { - border-bottom-left-radius: var(--bs-list-group-border-radius); - border-top-right-radius: 0; + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; } + .list-group-horizontal > .list-group-item:last-child:not(:first-child) { - border-top-right-radius: var(--bs-list-group-border-radius); - border-bottom-left-radius: 0; + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; } + .list-group-horizontal > .list-group-item.active { - margin-top: 0; + margin-top: 0; } + .list-group-horizontal > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-left-width: 0; + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; } + .list-group-horizontal > .list-group-item + .list-group-item.active { - margin-left: calc(-1 * var(--bs-list-group-border-width)); - border-left-width: var(--bs-list-group-border-width); + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); } @media (min-width: 576px) { - .list-group-horizontal-sm { - flex-direction: row; - } - .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) { - border-bottom-left-radius: var(--bs-list-group-border-radius); - border-top-right-radius: 0; - } - .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) { - border-top-right-radius: var(--bs-list-group-border-radius); - border-bottom-left-radius: 0; - } - .list-group-horizontal-sm > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-sm > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-left-width: 0; - } - .list-group-horizontal-sm > .list-group-item + .list-group-item.active { - margin-left: calc(-1 * var(--bs-list-group-border-width)); - border-left-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-sm { + flex-direction: row; + } + + .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; + } + + .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; + } + + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; + } + + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); + } } + @media (min-width: 768px) { - .list-group-horizontal-md { - flex-direction: row; - } - .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) { - border-bottom-left-radius: var(--bs-list-group-border-radius); - border-top-right-radius: 0; - } - .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) { - border-top-right-radius: var(--bs-list-group-border-radius); - border-bottom-left-radius: 0; - } - .list-group-horizontal-md > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-md > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-left-width: 0; - } - .list-group-horizontal-md > .list-group-item + .list-group-item.active { - margin-left: calc(-1 * var(--bs-list-group-border-width)); - border-left-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-md { + flex-direction: row; + } + + .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; + } + + .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; + } + + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; + } + + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); + } } + @media (min-width: 992px) { - .list-group-horizontal-lg { - flex-direction: row; - } - .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) { - border-bottom-left-radius: var(--bs-list-group-border-radius); - border-top-right-radius: 0; - } - .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) { - border-top-right-radius: var(--bs-list-group-border-radius); - border-bottom-left-radius: 0; - } - .list-group-horizontal-lg > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-lg > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-left-width: 0; - } - .list-group-horizontal-lg > .list-group-item + .list-group-item.active { - margin-left: calc(-1 * var(--bs-list-group-border-width)); - border-left-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-lg { + flex-direction: row; + } + + .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; + } + + .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; + } + + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; + } + + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); + } } + @media (min-width: 1200px) { - .list-group-horizontal-xl { - flex-direction: row; - } - .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) { - border-bottom-left-radius: var(--bs-list-group-border-radius); - border-top-right-radius: 0; - } - .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) { - border-top-right-radius: var(--bs-list-group-border-radius); - border-bottom-left-radius: 0; - } - .list-group-horizontal-xl > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-xl > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-left-width: 0; - } - .list-group-horizontal-xl > .list-group-item + .list-group-item.active { - margin-left: calc(-1 * var(--bs-list-group-border-width)); - border-left-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-xl { + flex-direction: row; + } + + .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; + } + + .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; + } + + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; + } + + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); + } } + @media (min-width: 1400px) { - .list-group-horizontal-xxl { - flex-direction: row; - } - .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) { - border-bottom-left-radius: var(--bs-list-group-border-radius); - border-top-right-radius: 0; - } - .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) { - border-top-right-radius: var(--bs-list-group-border-radius); - border-bottom-left-radius: 0; - } - .list-group-horizontal-xxl > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-xxl > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-left-width: 0; - } - .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { - margin-left: calc(-1 * var(--bs-list-group-border-width)); - border-left-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-xxl { + flex-direction: row; + } + + .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; + } + + .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; + } + + .list-group-horizontal-xxl > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-xxl > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; + } + + .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); + } } + .list-group-flush { - border-radius: 0; + border-radius: 0; } + .list-group-flush > .list-group-item { - border-width: 0 0 var(--bs-list-group-border-width); + border-width: 0 0 var(--bs-list-group-border-width); } + .list-group-flush > .list-group-item:last-child { - border-bottom-width: 0; + border-bottom-width: 0; } .list-group-item-primary { - --bs-list-group-color: var(--bs-primary-text-emphasis); - --bs-list-group-bg: var(--bs-primary-bg-subtle); - --bs-list-group-border-color: var(--bs-primary-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-primary-border-subtle); - --bs-list-group-active-color: var(--bs-primary-bg-subtle); - --bs-list-group-active-bg: var(--bs-primary-text-emphasis); - --bs-list-group-active-border-color: var(--bs-primary-text-emphasis); + --bs-list-group-color: var(--bs-primary-text-emphasis); + --bs-list-group-bg: var(--bs-primary-bg-subtle); + --bs-list-group-border-color: var(--bs-primary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-primary-border-subtle); + --bs-list-group-active-color: var(--bs-primary-bg-subtle); + --bs-list-group-active-bg: var(--bs-primary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-primary-text-emphasis); } .list-group-item-secondary { - --bs-list-group-color: var(--bs-secondary-text-emphasis); - --bs-list-group-bg: var(--bs-secondary-bg-subtle); - --bs-list-group-border-color: var(--bs-secondary-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle); - --bs-list-group-active-color: var(--bs-secondary-bg-subtle); - --bs-list-group-active-bg: var(--bs-secondary-text-emphasis); - --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis); + --bs-list-group-color: var(--bs-secondary-text-emphasis); + --bs-list-group-bg: var(--bs-secondary-bg-subtle); + --bs-list-group-border-color: var(--bs-secondary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle); + --bs-list-group-active-color: var(--bs-secondary-bg-subtle); + --bs-list-group-active-bg: var(--bs-secondary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis); } .list-group-item-success { - --bs-list-group-color: var(--bs-success-text-emphasis); - --bs-list-group-bg: var(--bs-success-bg-subtle); - --bs-list-group-border-color: var(--bs-success-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-success-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-success-border-subtle); - --bs-list-group-active-color: var(--bs-success-bg-subtle); - --bs-list-group-active-bg: var(--bs-success-text-emphasis); - --bs-list-group-active-border-color: var(--bs-success-text-emphasis); + --bs-list-group-color: var(--bs-success-text-emphasis); + --bs-list-group-bg: var(--bs-success-bg-subtle); + --bs-list-group-border-color: var(--bs-success-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-success-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-success-border-subtle); + --bs-list-group-active-color: var(--bs-success-bg-subtle); + --bs-list-group-active-bg: var(--bs-success-text-emphasis); + --bs-list-group-active-border-color: var(--bs-success-text-emphasis); } .list-group-item-info { - --bs-list-group-color: var(--bs-info-text-emphasis); - --bs-list-group-bg: var(--bs-info-bg-subtle); - --bs-list-group-border-color: var(--bs-info-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-info-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-info-border-subtle); - --bs-list-group-active-color: var(--bs-info-bg-subtle); - --bs-list-group-active-bg: var(--bs-info-text-emphasis); - --bs-list-group-active-border-color: var(--bs-info-text-emphasis); + --bs-list-group-color: var(--bs-info-text-emphasis); + --bs-list-group-bg: var(--bs-info-bg-subtle); + --bs-list-group-border-color: var(--bs-info-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-info-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-info-border-subtle); + --bs-list-group-active-color: var(--bs-info-bg-subtle); + --bs-list-group-active-bg: var(--bs-info-text-emphasis); + --bs-list-group-active-border-color: var(--bs-info-text-emphasis); } .list-group-item-warning { - --bs-list-group-color: var(--bs-warning-text-emphasis); - --bs-list-group-bg: var(--bs-warning-bg-subtle); - --bs-list-group-border-color: var(--bs-warning-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-warning-border-subtle); - --bs-list-group-active-color: var(--bs-warning-bg-subtle); - --bs-list-group-active-bg: var(--bs-warning-text-emphasis); - --bs-list-group-active-border-color: var(--bs-warning-text-emphasis); + --bs-list-group-color: var(--bs-warning-text-emphasis); + --bs-list-group-bg: var(--bs-warning-bg-subtle); + --bs-list-group-border-color: var(--bs-warning-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-warning-border-subtle); + --bs-list-group-active-color: var(--bs-warning-bg-subtle); + --bs-list-group-active-bg: var(--bs-warning-text-emphasis); + --bs-list-group-active-border-color: var(--bs-warning-text-emphasis); } .list-group-item-danger { - --bs-list-group-color: var(--bs-danger-text-emphasis); - --bs-list-group-bg: var(--bs-danger-bg-subtle); - --bs-list-group-border-color: var(--bs-danger-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-danger-border-subtle); - --bs-list-group-active-color: var(--bs-danger-bg-subtle); - --bs-list-group-active-bg: var(--bs-danger-text-emphasis); - --bs-list-group-active-border-color: var(--bs-danger-text-emphasis); + --bs-list-group-color: var(--bs-danger-text-emphasis); + --bs-list-group-bg: var(--bs-danger-bg-subtle); + --bs-list-group-border-color: var(--bs-danger-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-danger-border-subtle); + --bs-list-group-active-color: var(--bs-danger-bg-subtle); + --bs-list-group-active-bg: var(--bs-danger-text-emphasis); + --bs-list-group-active-border-color: var(--bs-danger-text-emphasis); } .list-group-item-light { - --bs-list-group-color: var(--bs-light-text-emphasis); - --bs-list-group-bg: var(--bs-light-bg-subtle); - --bs-list-group-border-color: var(--bs-light-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-light-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-light-border-subtle); - --bs-list-group-active-color: var(--bs-light-bg-subtle); - --bs-list-group-active-bg: var(--bs-light-text-emphasis); - --bs-list-group-active-border-color: var(--bs-light-text-emphasis); + --bs-list-group-color: var(--bs-light-text-emphasis); + --bs-list-group-bg: var(--bs-light-bg-subtle); + --bs-list-group-border-color: var(--bs-light-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-light-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-light-border-subtle); + --bs-list-group-active-color: var(--bs-light-bg-subtle); + --bs-list-group-active-bg: var(--bs-light-text-emphasis); + --bs-list-group-active-border-color: var(--bs-light-text-emphasis); } .list-group-item-dark { - --bs-list-group-color: var(--bs-dark-text-emphasis); - --bs-list-group-bg: var(--bs-dark-bg-subtle); - --bs-list-group-border-color: var(--bs-dark-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-dark-border-subtle); - --bs-list-group-active-color: var(--bs-dark-bg-subtle); - --bs-list-group-active-bg: var(--bs-dark-text-emphasis); - --bs-list-group-active-border-color: var(--bs-dark-text-emphasis); + --bs-list-group-color: var(--bs-dark-text-emphasis); + --bs-list-group-bg: var(--bs-dark-bg-subtle); + --bs-list-group-border-color: var(--bs-dark-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-dark-border-subtle); + --bs-list-group-active-color: var(--bs-dark-bg-subtle); + --bs-list-group-active-bg: var(--bs-dark-text-emphasis); + --bs-list-group-active-border-color: var(--bs-dark-text-emphasis); } .btn-close { - --bs-btn-close-color: #000; - --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); - --bs-btn-close-opacity: 0.5; - --bs-btn-close-hover-opacity: 0.75; - --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); - --bs-btn-close-focus-opacity: 1; - --bs-btn-close-disabled-opacity: 0.25; - --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); - box-sizing: content-box; - width: 1em; - height: 1em; - padding: 0.25em 0.25em; - color: var(--bs-btn-close-color); - background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; - border: 0; - border-radius: 0.375rem; - opacity: var(--bs-btn-close-opacity); + --bs-btn-close-color: #000; + --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); + --bs-btn-close-opacity: 0.5; + --bs-btn-close-hover-opacity: 0.75; + --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + --bs-btn-close-focus-opacity: 1; + --bs-btn-close-disabled-opacity: 0.25; + --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); + box-sizing: content-box; + width: 1em; + height: 1em; + padding: 0.25em 0.25em; + color: var(--bs-btn-close-color); + background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; + border: 0; + border-radius: 0.375rem; + opacity: var(--bs-btn-close-opacity); } + .btn-close:hover { - color: var(--bs-btn-close-color); - text-decoration: none; - opacity: var(--bs-btn-close-hover-opacity); + color: var(--bs-btn-close-color); + text-decoration: none; + opacity: var(--bs-btn-close-hover-opacity); } + .btn-close:focus { - outline: 0; - box-shadow: var(--bs-btn-close-focus-shadow); - opacity: var(--bs-btn-close-focus-opacity); + outline: 0; + box-shadow: var(--bs-btn-close-focus-shadow); + opacity: var(--bs-btn-close-focus-opacity); } + .btn-close:disabled, .btn-close.disabled { - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - opacity: var(--bs-btn-close-disabled-opacity); + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + opacity: var(--bs-btn-close-disabled-opacity); } .btn-close-white { - filter: var(--bs-btn-close-white-filter); + filter: var(--bs-btn-close-white-filter); } [data-bs-theme=dark] .btn-close { - filter: var(--bs-btn-close-white-filter); + filter: var(--bs-btn-close-white-filter); } .toast { - --bs-toast-zindex: 1090; - --bs-toast-padding-x: 0.75rem; - --bs-toast-padding-y: 0.5rem; - --bs-toast-spacing: 1.5rem; - --bs-toast-max-width: 350px; - --bs-toast-font-size: 0.875rem; - --bs-toast-color: ; - --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85); - --bs-toast-border-width: var(--bs-border-width); - --bs-toast-border-color: var(--bs-border-color-translucent); - --bs-toast-border-radius: var(--bs-border-radius); - --bs-toast-box-shadow: var(--bs-box-shadow); - --bs-toast-header-color: var(--bs-secondary-color); - --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85); - --bs-toast-header-border-color: var(--bs-border-color-translucent); - width: var(--bs-toast-max-width); - max-width: 100%; - font-size: var(--bs-toast-font-size); - color: var(--bs-toast-color); - pointer-events: auto; - background-color: var(--bs-toast-bg); - background-clip: padding-box; - border: var(--bs-toast-border-width) solid var(--bs-toast-border-color); - box-shadow: var(--bs-toast-box-shadow); - border-radius: var(--bs-toast-border-radius); + --bs-toast-zindex: 1090; + --bs-toast-padding-x: 0.75rem; + --bs-toast-padding-y: 0.5rem; + --bs-toast-spacing: 1.5rem; + --bs-toast-max-width: 350px; + --bs-toast-font-size: 0.875rem; + --bs-toast-color: ; + --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-border-width: var(--bs-border-width); + --bs-toast-border-color: var(--bs-border-color-translucent); + --bs-toast-border-radius: var(--bs-border-radius); + --bs-toast-box-shadow: var(--bs-box-shadow); + --bs-toast-header-color: var(--bs-secondary-color); + --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-header-border-color: var(--bs-border-color-translucent); + width: var(--bs-toast-max-width); + max-width: 100%; + font-size: var(--bs-toast-font-size); + color: var(--bs-toast-color); + pointer-events: auto; + background-color: var(--bs-toast-bg); + background-clip: padding-box; + border: var(--bs-toast-border-width) solid var(--bs-toast-border-color); + box-shadow: var(--bs-toast-box-shadow); + border-radius: var(--bs-toast-border-radius); } + .toast.showing { - opacity: 0; + opacity: 0; } + .toast:not(.show) { - display: none; + display: none; } .toast-container { - --bs-toast-zindex: 1090; - position: absolute; - z-index: var(--bs-toast-zindex); - width: -webkit-max-content; - width: -moz-max-content; - width: max-content; - max-width: 100%; - pointer-events: none; + --bs-toast-zindex: 1090; + position: absolute; + z-index: var(--bs-toast-zindex); + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + max-width: 100%; + pointer-events: none; } + .toast-container > :not(:last-child) { - margin-bottom: var(--bs-toast-spacing); + margin-bottom: var(--bs-toast-spacing); } .toast-header { - display: flex; - align-items: center; - padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); - color: var(--bs-toast-header-color); - background-color: var(--bs-toast-header-bg); - background-clip: padding-box; - border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color); - border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); - border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); + display: flex; + align-items: center; + padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); + color: var(--bs-toast-header-color); + background-color: var(--bs-toast-header-bg); + background-clip: padding-box; + border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color); + border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); + border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); } + .toast-header .btn-close { - margin-right: calc(-0.5 * var(--bs-toast-padding-x)); - margin-left: var(--bs-toast-padding-x); + margin-right: calc(-0.5 * var(--bs-toast-padding-x)); + margin-left: var(--bs-toast-padding-x); } .toast-body { - padding: var(--bs-toast-padding-x); - word-wrap: break-word; + padding: var(--bs-toast-padding-x); + word-wrap: break-word; } .modal { - --bs-modal-zindex: 1055; - --bs-modal-width: 500px; - --bs-modal-padding: 1rem; - --bs-modal-margin: 0.5rem; - --bs-modal-color: ; - --bs-modal-bg: var(--bs-body-bg); - --bs-modal-border-color: var(--bs-border-color-translucent); - --bs-modal-border-width: var(--bs-border-width); - --bs-modal-border-radius: var(--bs-border-radius-lg); - --bs-modal-box-shadow: var(--bs-box-shadow-sm); - --bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width))); - --bs-modal-header-padding-x: 1rem; - --bs-modal-header-padding-y: 1rem; - --bs-modal-header-padding: 1rem 1rem; - --bs-modal-header-border-color: var(--bs-border-color); - --bs-modal-header-border-width: var(--bs-border-width); - --bs-modal-title-line-height: 1.5; - --bs-modal-footer-gap: 0.5rem; - --bs-modal-footer-bg: ; - --bs-modal-footer-border-color: var(--bs-border-color); - --bs-modal-footer-border-width: var(--bs-border-width); - position: fixed; - top: 0; - left: 0; - z-index: var(--bs-modal-zindex); - display: none; - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - outline: 0; + --bs-modal-zindex: 1055; + --bs-modal-width: 500px; + --bs-modal-padding: 1rem; + --bs-modal-margin: 0.5rem; + --bs-modal-color: ; + --bs-modal-bg: var(--bs-body-bg); + --bs-modal-border-color: var(--bs-border-color-translucent); + --bs-modal-border-width: var(--bs-border-width); + --bs-modal-border-radius: var(--bs-border-radius-lg); + --bs-modal-box-shadow: var(--bs-box-shadow-sm); + --bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width))); + --bs-modal-header-padding-x: 1rem; + --bs-modal-header-padding-y: 1rem; + --bs-modal-header-padding: 1rem 1rem; + --bs-modal-header-border-color: var(--bs-border-color); + --bs-modal-header-border-width: var(--bs-border-width); + --bs-modal-title-line-height: 1.5; + --bs-modal-footer-gap: 0.5rem; + --bs-modal-footer-bg: ; + --bs-modal-footer-border-color: var(--bs-border-color); + --bs-modal-footer-border-width: var(--bs-border-width); + position: fixed; + top: 0; + left: 0; + z-index: var(--bs-modal-zindex); + display: none; + width: 100%; + height: 100%; + overflow-x: hidden; + overflow-y: auto; + outline: 0; } .modal-dialog { - position: relative; - width: auto; - margin: var(--bs-modal-margin); - pointer-events: none; + position: relative; + width: auto; + margin: var(--bs-modal-margin); + pointer-events: none; } + .modal.fade .modal-dialog { - transition: transform 0.3s ease-out; - transform: translate(0, -50px); + transition: transform 0.3s ease-out; + transform: translate(0, -50px); } + @media (prefers-reduced-motion: reduce) { - .modal.fade .modal-dialog { - transition: none; - } + .modal.fade .modal-dialog { + transition: none; + } } + .modal.show .modal-dialog { - transform: none; + transform: none; } + .modal.modal-static .modal-dialog { - transform: scale(1.02); + transform: scale(1.02); } .modal-dialog-scrollable { - height: calc(100% - var(--bs-modal-margin) * 2); + height: calc(100% - var(--bs-modal-margin) * 2); } + .modal-dialog-scrollable .modal-content { - max-height: 100%; - overflow: hidden; + max-height: 100%; + overflow: hidden; } + .modal-dialog-scrollable .modal-body { - overflow-y: auto; + overflow-y: auto; } .modal-dialog-centered { - display: flex; - align-items: center; - min-height: calc(100% - var(--bs-modal-margin) * 2); + display: flex; + align-items: center; + min-height: calc(100% - var(--bs-modal-margin) * 2); } .modal-content { - position: relative; - display: flex; - flex-direction: column; - width: 100%; - color: var(--bs-modal-color); - pointer-events: auto; - background-color: var(--bs-modal-bg); - background-clip: padding-box; - border: var(--bs-modal-border-width) solid var(--bs-modal-border-color); - border-radius: var(--bs-modal-border-radius); - outline: 0; + position: relative; + display: flex; + flex-direction: column; + width: 100%; + color: var(--bs-modal-color); + pointer-events: auto; + background-color: var(--bs-modal-bg); + background-clip: padding-box; + border: var(--bs-modal-border-width) solid var(--bs-modal-border-color); + border-radius: var(--bs-modal-border-radius); + outline: 0; } .modal-backdrop { - --bs-backdrop-zindex: 1050; - --bs-backdrop-bg: #000; - --bs-backdrop-opacity: 0.5; - position: fixed; - top: 0; - left: 0; - z-index: var(--bs-backdrop-zindex); - width: 100vw; - height: 100vh; - background-color: var(--bs-backdrop-bg); + --bs-backdrop-zindex: 1050; + --bs-backdrop-bg: #000; + --bs-backdrop-opacity: 0.5; + position: fixed; + top: 0; + left: 0; + z-index: var(--bs-backdrop-zindex); + width: 100vw; + height: 100vh; + background-color: var(--bs-backdrop-bg); } + .modal-backdrop.fade { - opacity: 0; + opacity: 0; } + .modal-backdrop.show { - opacity: var(--bs-backdrop-opacity); + opacity: var(--bs-backdrop-opacity); } .modal-header { - display: flex; - flex-shrink: 0; - align-items: center; - padding: var(--bs-modal-header-padding); - border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); - border-top-left-radius: var(--bs-modal-inner-border-radius); - border-top-right-radius: var(--bs-modal-inner-border-radius); + display: flex; + flex-shrink: 0; + align-items: center; + padding: var(--bs-modal-header-padding); + border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); + border-top-left-radius: var(--bs-modal-inner-border-radius); + border-top-right-radius: var(--bs-modal-inner-border-radius); } + .modal-header .btn-close { - padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5); - margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) auto; + padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5); + margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) auto; } .modal-title { - margin-bottom: 0; - line-height: var(--bs-modal-title-line-height); + margin-bottom: 0; + line-height: var(--bs-modal-title-line-height); } .modal-body { - position: relative; - flex: 1 1 auto; - padding: var(--bs-modal-padding); + position: relative; + flex: 1 1 auto; + padding: var(--bs-modal-padding); } .modal-footer { - display: flex; - flex-shrink: 0; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; - padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5); - background-color: var(--bs-modal-footer-bg); - border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); - border-bottom-right-radius: var(--bs-modal-inner-border-radius); - border-bottom-left-radius: var(--bs-modal-inner-border-radius); + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5); + background-color: var(--bs-modal-footer-bg); + border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); + border-bottom-right-radius: var(--bs-modal-inner-border-radius); + border-bottom-left-radius: var(--bs-modal-inner-border-radius); } + .modal-footer > * { - margin: calc(var(--bs-modal-footer-gap) * 0.5); + margin: calc(var(--bs-modal-footer-gap) * 0.5); } @media (min-width: 576px) { - .modal { - --bs-modal-margin: 1.75rem; - --bs-modal-box-shadow: var(--bs-box-shadow); - } - .modal-dialog { - max-width: var(--bs-modal-width); - margin-right: auto; - margin-left: auto; - } - .modal-sm { - --bs-modal-width: 300px; - } + .modal { + --bs-modal-margin: 1.75rem; + --bs-modal-box-shadow: var(--bs-box-shadow); + } + + .modal-dialog { + max-width: var(--bs-modal-width); + margin-right: auto; + margin-left: auto; + } + + .modal-sm { + --bs-modal-width: 300px; + } } + @media (min-width: 992px) { - .modal-lg, - .modal-xl { - --bs-modal-width: 800px; - } + .modal-lg, + .modal-xl { + --bs-modal-width: 800px; + } } + @media (min-width: 1200px) { - .modal-xl { - --bs-modal-width: 1140px; - } + .modal-xl { + --bs-modal-width: 1140px; + } } + .modal-fullscreen { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; + width: 100vw; + max-width: none; + height: 100%; + margin: 0; } + .modal-fullscreen .modal-content { - height: 100%; - border: 0; - border-radius: 0; + height: 100%; + border: 0; + border-radius: 0; } + .modal-fullscreen .modal-header, .modal-fullscreen .modal-footer { - border-radius: 0; + border-radius: 0; } + .modal-fullscreen .modal-body { - overflow-y: auto; + overflow-y: auto; } @media (max-width: 575.98px) { - .modal-fullscreen-sm-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-sm-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-sm-down .modal-header, - .modal-fullscreen-sm-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-sm-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-sm-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-sm-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-sm-down .modal-header, + .modal-fullscreen-sm-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-sm-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 767.98px) { - .modal-fullscreen-md-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-md-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-md-down .modal-header, - .modal-fullscreen-md-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-md-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-md-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-md-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-md-down .modal-header, + .modal-fullscreen-md-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-md-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 991.98px) { - .modal-fullscreen-lg-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-lg-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-lg-down .modal-header, - .modal-fullscreen-lg-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-lg-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-lg-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-lg-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-lg-down .modal-header, + .modal-fullscreen-lg-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-lg-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 1199.98px) { - .modal-fullscreen-xl-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-xl-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-xl-down .modal-header, - .modal-fullscreen-xl-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-xl-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-xl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-xl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-xl-down .modal-header, + .modal-fullscreen-xl-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-xl-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 1399.98px) { - .modal-fullscreen-xxl-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-xxl-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-xxl-down .modal-header, - .modal-fullscreen-xxl-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-xxl-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-xxl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-xxl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-xxl-down .modal-header, + .modal-fullscreen-xxl-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-xxl-down .modal-body { + overflow-y: auto; + } } + .tooltip { - --bs-tooltip-zindex: 1080; - --bs-tooltip-max-width: 200px; - --bs-tooltip-padding-x: 0.5rem; - --bs-tooltip-padding-y: 0.25rem; - --bs-tooltip-margin: ; - --bs-tooltip-font-size: 0.875rem; - --bs-tooltip-color: var(--bs-body-bg); - --bs-tooltip-bg: var(--bs-emphasis-color); - --bs-tooltip-border-radius: var(--bs-border-radius); - --bs-tooltip-opacity: 0.9; - --bs-tooltip-arrow-width: 0.8rem; - --bs-tooltip-arrow-height: 0.4rem; - z-index: var(--bs-tooltip-zindex); - display: block; - margin: var(--bs-tooltip-margin); - font-family: var(--bs-font-sans-serif); - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - white-space: normal; - word-spacing: normal; - line-break: auto; - font-size: var(--bs-tooltip-font-size); - word-wrap: break-word; - opacity: 0; + --bs-tooltip-zindex: 1080; + --bs-tooltip-max-width: 200px; + --bs-tooltip-padding-x: 0.5rem; + --bs-tooltip-padding-y: 0.25rem; + --bs-tooltip-margin: ; + --bs-tooltip-font-size: 0.875rem; + --bs-tooltip-color: var(--bs-body-bg); + --bs-tooltip-bg: var(--bs-emphasis-color); + --bs-tooltip-border-radius: var(--bs-border-radius); + --bs-tooltip-opacity: 0.9; + --bs-tooltip-arrow-width: 0.8rem; + --bs-tooltip-arrow-height: 0.4rem; + z-index: var(--bs-tooltip-zindex); + display: block; + margin: var(--bs-tooltip-margin); + font-family: var(--bs-font-sans-serif); + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + white-space: normal; + word-spacing: normal; + line-break: auto; + font-size: var(--bs-tooltip-font-size); + word-wrap: break-word; + opacity: 0; } + .tooltip.show { - opacity: var(--bs-tooltip-opacity); + opacity: var(--bs-tooltip-opacity); } + .tooltip .tooltip-arrow { - display: block; - width: var(--bs-tooltip-arrow-width); - height: var(--bs-tooltip-arrow-height); + display: block; + width: var(--bs-tooltip-arrow-width); + height: var(--bs-tooltip-arrow-height); } + .tooltip .tooltip-arrow::before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid; + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; } .bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { - bottom: calc(-1 * var(--bs-tooltip-arrow-height)); + bottom: calc(-1 * var(--bs-tooltip-arrow-height)); } + .bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { - top: -1px; - border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; - border-top-color: var(--bs-tooltip-bg); + top: -1px; + border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; + border-top-color: var(--bs-tooltip-bg); } /* rtl:begin:ignore */ .bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { - left: calc(-1 * var(--bs-tooltip-arrow-height)); - width: var(--bs-tooltip-arrow-height); - height: var(--bs-tooltip-arrow-width); + left: calc(-1 * var(--bs-tooltip-arrow-height)); + width: var(--bs-tooltip-arrow-height); + height: var(--bs-tooltip-arrow-width); } + .bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before { - right: -1px; - border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; - border-right-color: var(--bs-tooltip-bg); + right: -1px; + border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; + border-right-color: var(--bs-tooltip-bg); } /* rtl:end:ignore */ .bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { - top: calc(-1 * var(--bs-tooltip-arrow-height)); + top: calc(-1 * var(--bs-tooltip-arrow-height)); } + .bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { - bottom: -1px; - border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); - border-bottom-color: var(--bs-tooltip-bg); + bottom: -1px; + border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); + border-bottom-color: var(--bs-tooltip-bg); } /* rtl:begin:ignore */ .bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { - right: calc(-1 * var(--bs-tooltip-arrow-height)); - width: var(--bs-tooltip-arrow-height); - height: var(--bs-tooltip-arrow-width); + right: calc(-1 * var(--bs-tooltip-arrow-height)); + width: var(--bs-tooltip-arrow-height); + height: var(--bs-tooltip-arrow-width); } + .bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before { - left: -1px; - border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); - border-left-color: var(--bs-tooltip-bg); + left: -1px; + border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); + border-left-color: var(--bs-tooltip-bg); } /* rtl:end:ignore */ .tooltip-inner { - max-width: var(--bs-tooltip-max-width); - padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x); - color: var(--bs-tooltip-color); - text-align: center; - background-color: var(--bs-tooltip-bg); - border-radius: var(--bs-tooltip-border-radius); + max-width: var(--bs-tooltip-max-width); + padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x); + color: var(--bs-tooltip-color); + text-align: center; + background-color: var(--bs-tooltip-bg); + border-radius: var(--bs-tooltip-border-radius); } .popover { - --bs-popover-zindex: 1070; - --bs-popover-max-width: 276px; - --bs-popover-font-size: 0.875rem; - --bs-popover-bg: var(--bs-body-bg); - --bs-popover-border-width: var(--bs-border-width); - --bs-popover-border-color: var(--bs-border-color-translucent); - --bs-popover-border-radius: var(--bs-border-radius-lg); - --bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width)); - --bs-popover-box-shadow: var(--bs-box-shadow); - --bs-popover-header-padding-x: 1rem; - --bs-popover-header-padding-y: 0.5rem; - --bs-popover-header-font-size: 1rem; - --bs-popover-header-color: inherit; - --bs-popover-header-bg: var(--bs-secondary-bg); - --bs-popover-body-padding-x: 1rem; - --bs-popover-body-padding-y: 1rem; - --bs-popover-body-color: var(--bs-body-color); - --bs-popover-arrow-width: 1rem; - --bs-popover-arrow-height: 0.5rem; - --bs-popover-arrow-border: var(--bs-popover-border-color); - z-index: var(--bs-popover-zindex); - display: block; - max-width: var(--bs-popover-max-width); - font-family: var(--bs-font-sans-serif); - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - white-space: normal; - word-spacing: normal; - line-break: auto; - font-size: var(--bs-popover-font-size); - word-wrap: break-word; - background-color: var(--bs-popover-bg); - background-clip: padding-box; - border: var(--bs-popover-border-width) solid var(--bs-popover-border-color); - border-radius: var(--bs-popover-border-radius); + --bs-popover-zindex: 1070; + --bs-popover-max-width: 276px; + --bs-popover-font-size: 0.875rem; + --bs-popover-bg: var(--bs-body-bg); + --bs-popover-border-width: var(--bs-border-width); + --bs-popover-border-color: var(--bs-border-color-translucent); + --bs-popover-border-radius: var(--bs-border-radius-lg); + --bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width)); + --bs-popover-box-shadow: var(--bs-box-shadow); + --bs-popover-header-padding-x: 1rem; + --bs-popover-header-padding-y: 0.5rem; + --bs-popover-header-font-size: 1rem; + --bs-popover-header-color: inherit; + --bs-popover-header-bg: var(--bs-secondary-bg); + --bs-popover-body-padding-x: 1rem; + --bs-popover-body-padding-y: 1rem; + --bs-popover-body-color: var(--bs-body-color); + --bs-popover-arrow-width: 1rem; + --bs-popover-arrow-height: 0.5rem; + --bs-popover-arrow-border: var(--bs-popover-border-color); + z-index: var(--bs-popover-zindex); + display: block; + max-width: var(--bs-popover-max-width); + font-family: var(--bs-font-sans-serif); + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + white-space: normal; + word-spacing: normal; + line-break: auto; + font-size: var(--bs-popover-font-size); + word-wrap: break-word; + background-color: var(--bs-popover-bg); + background-clip: padding-box; + border: var(--bs-popover-border-width) solid var(--bs-popover-border-color); + border-radius: var(--bs-popover-border-radius); } + .popover .popover-arrow { - display: block; - width: var(--bs-popover-arrow-width); - height: var(--bs-popover-arrow-height); + display: block; + width: var(--bs-popover-arrow-width); + height: var(--bs-popover-arrow-height); } + .popover .popover-arrow::before, .popover .popover-arrow::after { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid; - border-width: 0; + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; + border-width: 0; } .bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow { - bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } + .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { - border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; + border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } + .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before { - bottom: 0; - border-top-color: var(--bs-popover-arrow-border); + bottom: 0; + border-top-color: var(--bs-popover-arrow-border); } + .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { - bottom: var(--bs-popover-border-width); - border-top-color: var(--bs-popover-bg); + bottom: var(--bs-popover-border-width); + border-top-color: var(--bs-popover-bg); } /* rtl:begin:ignore */ .bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow { - left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); - width: var(--bs-popover-arrow-height); - height: var(--bs-popover-arrow-width); + left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + width: var(--bs-popover-arrow-height); + height: var(--bs-popover-arrow-width); } + .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { - border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; + border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } + .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before { - left: 0; - border-right-color: var(--bs-popover-arrow-border); + left: 0; + border-right-color: var(--bs-popover-arrow-border); } + .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { - left: var(--bs-popover-border-width); - border-right-color: var(--bs-popover-bg); + left: var(--bs-popover-border-width); + border-right-color: var(--bs-popover-bg); } /* rtl:end:ignore */ .bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow { - top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } + .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { - border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); + border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } + .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before { - top: 0; - border-bottom-color: var(--bs-popover-arrow-border); + top: 0; + border-bottom-color: var(--bs-popover-arrow-border); } + .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { - top: var(--bs-popover-border-width); - border-bottom-color: var(--bs-popover-bg); + top: var(--bs-popover-border-width); + border-bottom-color: var(--bs-popover-bg); } + .bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: var(--bs-popover-arrow-width); - margin-left: calc(-0.5 * var(--bs-popover-arrow-width)); - content: ""; - border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg); + position: absolute; + top: 0; + left: 50%; + display: block; + width: var(--bs-popover-arrow-width); + margin-left: calc(-0.5 * var(--bs-popover-arrow-width)); + content: ""; + border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg); } /* rtl:begin:ignore */ .bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow { - right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); - width: var(--bs-popover-arrow-height); - height: var(--bs-popover-arrow-width); + right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + width: var(--bs-popover-arrow-height); + height: var(--bs-popover-arrow-width); } + .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { - border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); + border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } + .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before { - right: 0; - border-left-color: var(--bs-popover-arrow-border); + right: 0; + border-left-color: var(--bs-popover-arrow-border); } + .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { - right: var(--bs-popover-border-width); - border-left-color: var(--bs-popover-bg); + right: var(--bs-popover-border-width); + border-left-color: var(--bs-popover-bg); } /* rtl:end:ignore */ .popover-header { - padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x); - margin-bottom: 0; - font-size: var(--bs-popover-header-font-size); - color: var(--bs-popover-header-color); - background-color: var(--bs-popover-header-bg); - border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color); - border-top-left-radius: var(--bs-popover-inner-border-radius); - border-top-right-radius: var(--bs-popover-inner-border-radius); + padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x); + margin-bottom: 0; + font-size: var(--bs-popover-header-font-size); + color: var(--bs-popover-header-color); + background-color: var(--bs-popover-header-bg); + border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color); + border-top-left-radius: var(--bs-popover-inner-border-radius); + border-top-right-radius: var(--bs-popover-inner-border-radius); } + .popover-header:empty { - display: none; + display: none; } .popover-body { - padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x); - color: var(--bs-popover-body-color); + padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x); + color: var(--bs-popover-body-color); } .carousel { - position: relative; + position: relative; } .carousel.pointer-event { - touch-action: pan-y; + touch-action: pan-y; } .carousel-inner { - position: relative; - width: 100%; - overflow: hidden; + position: relative; + width: 100%; + overflow: hidden; } + .carousel-inner::after { - display: block; - clear: both; - content: ""; + display: block; + clear: both; + content: ""; } .carousel-item { - position: relative; - display: none; - float: left; - width: 100%; - margin-right: -100%; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - transition: transform 0.6s ease-in-out; + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition: transform 0.6s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .carousel-item { - transition: none; - } + .carousel-item { + transition: none; + } } .carousel-item.active, .carousel-item-next, .carousel-item-prev { - display: block; + display: block; } .carousel-item-next:not(.carousel-item-start), .active.carousel-item-end { - transform: translateX(100%); + transform: translateX(100%); } .carousel-item-prev:not(.carousel-item-end), .active.carousel-item-start { - transform: translateX(-100%); + transform: translateX(-100%); } .carousel-fade .carousel-item { - opacity: 0; - transition-property: opacity; - transform: none; + opacity: 0; + transition-property: opacity; + transform: none; } + .carousel-fade .carousel-item.active, .carousel-fade .carousel-item-next.carousel-item-start, .carousel-fade .carousel-item-prev.carousel-item-end { - z-index: 1; - opacity: 1; + z-index: 1; + opacity: 1; } + .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { - z-index: 0; - opacity: 0; - transition: opacity 0s 0.6s; + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; } + @media (prefers-reduced-motion: reduce) { - .carousel-fade .active.carousel-item-start, - .carousel-fade .active.carousel-item-end { - transition: none; - } + .carousel-fade .active.carousel-item-start, + .carousel-fade .active.carousel-item-end { + transition: none; + } } .carousel-control-prev, .carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - z-index: 1; - display: flex; - align-items: center; - justify-content: center; - width: 15%; - padding: 0; - color: #fff; - text-align: center; - background: none; - border: 0; - opacity: 0.5; - transition: opacity 0.15s ease; + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + padding: 0; + color: #fff; + text-align: center; + background: none; + border: 0; + opacity: 0.5; + transition: opacity 0.15s ease; } + @media (prefers-reduced-motion: reduce) { - .carousel-control-prev, - .carousel-control-next { - transition: none; - } + .carousel-control-prev, + .carousel-control-next { + transition: none; + } } + .carousel-control-prev:hover, .carousel-control-prev:focus, .carousel-control-next:hover, .carousel-control-next:focus { - color: #fff; - text-decoration: none; - outline: 0; - opacity: 0.9; + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; } .carousel-control-prev { - left: 0; + left: 0; } .carousel-control-next { - right: 0; + right: 0; } .carousel-control-prev-icon, .carousel-control-next-icon { - display: inline-block; - width: 2rem; - height: 2rem; - background-repeat: no-repeat; - background-position: 50%; - background-size: 100% 100%; + display: inline-block; + width: 2rem; + height: 2rem; + background-repeat: no-repeat; + background-position: 50%; + background-size: 100% 100%; } .carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")*/; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")*/; } .carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")*/; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")*/; } .carousel-indicators { - position: absolute; - right: 0; - bottom: 0; - left: 0; - z-index: 2; - display: flex; - justify-content: center; - padding: 0; - margin-right: 15%; - margin-bottom: 1rem; - margin-left: 15%; + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 2; + display: flex; + justify-content: center; + padding: 0; + margin-right: 15%; + margin-bottom: 1rem; + margin-left: 15%; } + .carousel-indicators [data-bs-target] { - box-sizing: content-box; - flex: 0 1 auto; - width: 30px; - height: 3px; - padding: 0; - margin-right: 3px; - margin-left: 3px; - text-indent: -999px; - cursor: pointer; - background-color: #fff; - background-clip: padding-box; - border: 0; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - opacity: 0.5; - transition: opacity 0.6s ease; + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + padding: 0; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: 0.5; + transition: opacity 0.6s ease; } + @media (prefers-reduced-motion: reduce) { - .carousel-indicators [data-bs-target] { - transition: none; - } + .carousel-indicators [data-bs-target] { + transition: none; + } } + .carousel-indicators .active { - opacity: 1; + opacity: 1; } .carousel-caption { - position: absolute; - right: 15%; - bottom: 1.25rem; - left: 15%; - padding-top: 1.25rem; - padding-bottom: 1.25rem; - color: #fff; - text-align: center; + position: absolute; + right: 15%; + bottom: 1.25rem; + left: 15%; + padding-top: 1.25rem; + padding-bottom: 1.25rem; + color: #fff; + text-align: center; } .carousel-dark .carousel-control-prev-icon, .carousel-dark .carousel-control-next-icon { - filter: invert(1) grayscale(100); + filter: invert(1) grayscale(100); } + .carousel-dark .carousel-indicators [data-bs-target] { - background-color: #000; + background-color: #000; } + .carousel-dark .carousel-caption { - color: #000; + color: #000; } [data-bs-theme=dark] .carousel .carousel-control-prev-icon, [data-bs-theme=dark] .carousel .carousel-control-next-icon, [data-bs-theme=dark].carousel .carousel-control-prev-icon, [data-bs-theme=dark].carousel .carousel-control-next-icon { - filter: invert(1) grayscale(100); + filter: invert(1) grayscale(100); } + [data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target], [data-bs-theme=dark].carousel .carousel-indicators [data-bs-target] { - background-color: #000; + background-color: #000; } + [data-bs-theme=dark] .carousel .carousel-caption, [data-bs-theme=dark].carousel .carousel-caption { - color: #000; + color: #000; } .spinner-grow, .spinner-border { - display: inline-block; - width: var(--bs-spinner-width); - height: var(--bs-spinner-height); - vertical-align: var(--bs-spinner-vertical-align); - border-radius: 50%; - animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name); + display: inline-block; + width: var(--bs-spinner-width); + height: var(--bs-spinner-height); + vertical-align: var(--bs-spinner-vertical-align); + border-radius: 50%; + animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name); } @keyframes spinner-border { - to { - transform: rotate(360deg) /* rtl:ignore */; - } + to { + transform: rotate(360deg) /* rtl:ignore */; + } } + .spinner-border { - --bs-spinner-width: 2rem; - --bs-spinner-height: 2rem; - --bs-spinner-vertical-align: -0.125em; - --bs-spinner-border-width: 0.25em; - --bs-spinner-animation-speed: 0.75s; - --bs-spinner-animation-name: spinner-border; - border: var(--bs-spinner-border-width) solid currentcolor; - border-right-color: transparent; + --bs-spinner-width: 2rem; + --bs-spinner-height: 2rem; + --bs-spinner-vertical-align: -0.125em; + --bs-spinner-border-width: 0.25em; + --bs-spinner-animation-speed: 0.75s; + --bs-spinner-animation-name: spinner-border; + border: var(--bs-spinner-border-width) solid currentcolor; + border-right-color: transparent; } .spinner-border-sm { - --bs-spinner-width: 1rem; - --bs-spinner-height: 1rem; - --bs-spinner-border-width: 0.2em; + --bs-spinner-width: 1rem; + --bs-spinner-height: 1rem; + --bs-spinner-border-width: 0.2em; } @keyframes spinner-grow { - 0% { - transform: scale(0); - } - 50% { - opacity: 1; - transform: none; - } + 0% { + transform: scale(0); + } + 50% { + opacity: 1; + transform: none; + } } + .spinner-grow { - --bs-spinner-width: 2rem; - --bs-spinner-height: 2rem; - --bs-spinner-vertical-align: -0.125em; - --bs-spinner-animation-speed: 0.75s; - --bs-spinner-animation-name: spinner-grow; - background-color: currentcolor; - opacity: 0; + --bs-spinner-width: 2rem; + --bs-spinner-height: 2rem; + --bs-spinner-vertical-align: -0.125em; + --bs-spinner-animation-speed: 0.75s; + --bs-spinner-animation-name: spinner-grow; + background-color: currentcolor; + opacity: 0; } .spinner-grow-sm { - --bs-spinner-width: 1rem; - --bs-spinner-height: 1rem; + --bs-spinner-width: 1rem; + --bs-spinner-height: 1rem; } @media (prefers-reduced-motion: reduce) { - .spinner-border, - .spinner-grow { - --bs-spinner-animation-speed: 1.5s; - } + .spinner-border, + .spinner-grow { + --bs-spinner-animation-speed: 1.5s; + } } + .offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm { - --bs-offcanvas-zindex: 1045; - --bs-offcanvas-width: 400px; - --bs-offcanvas-height: 30vh; - --bs-offcanvas-padding-x: 1rem; - --bs-offcanvas-padding-y: 1rem; - --bs-offcanvas-color: var(--bs-body-color); - --bs-offcanvas-bg: var(--bs-body-bg); - --bs-offcanvas-border-width: var(--bs-border-width); - --bs-offcanvas-border-color: var(--bs-border-color-translucent); - --bs-offcanvas-box-shadow: var(--bs-box-shadow-sm); - --bs-offcanvas-transition: transform 0.3s ease-in-out; - --bs-offcanvas-title-line-height: 1.5; + --bs-offcanvas-zindex: 1045; + --bs-offcanvas-width: 400px; + --bs-offcanvas-height: 30vh; + --bs-offcanvas-padding-x: 1rem; + --bs-offcanvas-padding-y: 1rem; + --bs-offcanvas-color: var(--bs-body-color); + --bs-offcanvas-bg: var(--bs-body-bg); + --bs-offcanvas-border-width: var(--bs-border-width); + --bs-offcanvas-border-color: var(--bs-border-color-translucent); + --bs-offcanvas-box-shadow: var(--bs-box-shadow-sm); + --bs-offcanvas-transition: transform 0.3s ease-in-out; + --bs-offcanvas-title-line-height: 1.5; +} + +@media (max-width: 575.98px) { + .offcanvas-sm { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-sm { + transition: none; + } } @media (max-width: 575.98px) { - .offcanvas-sm { + .offcanvas-sm.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-sm.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-sm.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-sm.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) { + transform: none; + } + + .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show { + visibility: visible; + } +} + +@media (min-width: 576px) { + .offcanvas-sm { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-sm .offcanvas-header { + display: none; + } + + .offcanvas-sm .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 767.98px) { + .offcanvas-md { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-md { + transition: none; + } +} + +@media (max-width: 767.98px) { + .offcanvas-md.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-md.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-md.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-md.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) { + transform: none; + } + + .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show { + visibility: visible; + } +} + +@media (min-width: 768px) { + .offcanvas-md { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-md .offcanvas-header { + display: none; + } + + .offcanvas-md .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 991.98px) { + .offcanvas-lg { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-lg { + transition: none; + } +} + +@media (max-width: 991.98px) { + .offcanvas-lg.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-lg.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-lg.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-lg.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) { + transform: none; + } + + .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show { + visibility: visible; + } +} + +@media (min-width: 992px) { + .offcanvas-lg { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-lg .offcanvas-header { + display: none; + } + + .offcanvas-lg .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 1199.98px) { + .offcanvas-xl { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-xl { + transition: none; + } +} + +@media (max-width: 1199.98px) { + .offcanvas-xl.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-xl.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-xl.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-xl.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) { + transform: none; + } + + .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show { + visibility: visible; + } +} + +@media (min-width: 1200px) { + .offcanvas-xl { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-xl .offcanvas-header { + display: none; + } + + .offcanvas-xl .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 1399.98px) { + .offcanvas-xxl { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-xxl { + transition: none; + } +} + +@media (max-width: 1399.98px) { + .offcanvas-xxl.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-xxl.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-xxl.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-xxl.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) { + transform: none; + } + + .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show { + visibility: visible; + } +} + +@media (min-width: 1400px) { + .offcanvas-xxl { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-xxl .offcanvas-header { + display: none; + } + + .offcanvas-xxl .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +.offcanvas { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); @@ -6317,29 +7449,31 @@ textarea.form-control-lg { background-clip: padding-box; outline: 0; transition: var(--bs-offcanvas-transition); - } } -@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-sm { - transition: none; - } + +@media (prefers-reduced-motion: reduce) { + .offcanvas { + transition: none; + } } -@media (max-width: 575.98px) { - .offcanvas-sm.offcanvas-start { + +.offcanvas.offcanvas-start { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); - } - .offcanvas-sm.offcanvas-end { +} + +.offcanvas.offcanvas-end { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); - } - .offcanvas-sm.offcanvas-top { +} + +.offcanvas.offcanvas-top { top: 0; right: 0; left: 0; @@ -6347,5711 +7481,6241 @@ textarea.form-control-lg { max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); - } - .offcanvas-sm.offcanvas-bottom { +} + +.offcanvas.offcanvas-bottom { right: 0; left: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); - } - .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) { +} + +.offcanvas.showing, .offcanvas.show:not(.hiding) { transform: none; - } - .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show { - visibility: visible; - } } -@media (min-width: 576px) { - .offcanvas-sm { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-sm .offcanvas-header { - display: none; - } - .offcanvas-sm .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } + +.offcanvas.showing, .offcanvas.hiding, .offcanvas.show { + visibility: visible; } -@media (max-width: 767.98px) { - .offcanvas-md { +.offcanvas-backdrop { position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-md { - transition: none; - } -} -@media (max-width: 767.98px) { - .offcanvas-md.offcanvas-start { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-md.offcanvas-end { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-md.offcanvas-top { - top: 0; - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-md.offcanvas-bottom { - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) { - transform: none; - } - .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show { - visibility: visible; - } -} -@media (min-width: 768px) { - .offcanvas-md { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-md .offcanvas-header { - display: none; - } - .offcanvas-md .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } -} - -@media (max-width: 991.98px) { - .offcanvas-lg { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-lg { - transition: none; - } -} -@media (max-width: 991.98px) { - .offcanvas-lg.offcanvas-start { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-lg.offcanvas-end { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-lg.offcanvas-top { - top: 0; - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-lg.offcanvas-bottom { - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) { - transform: none; - } - .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show { - visibility: visible; - } -} -@media (min-width: 992px) { - .offcanvas-lg { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-lg .offcanvas-header { - display: none; - } - .offcanvas-lg .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } -} - -@media (max-width: 1199.98px) { - .offcanvas-xl { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-xl { - transition: none; - } -} -@media (max-width: 1199.98px) { - .offcanvas-xl.offcanvas-start { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-xl.offcanvas-end { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-xl.offcanvas-top { - top: 0; - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-xl.offcanvas-bottom { - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) { - transform: none; - } - .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show { - visibility: visible; - } -} -@media (min-width: 1200px) { - .offcanvas-xl { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-xl .offcanvas-header { - display: none; - } - .offcanvas-xl .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } -} - -@media (max-width: 1399.98px) { - .offcanvas-xxl { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-xxl { - transition: none; - } -} -@media (max-width: 1399.98px) { - .offcanvas-xxl.offcanvas-start { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-xxl.offcanvas-end { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-xxl.offcanvas-top { top: 0; - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-xxl.offcanvas-bottom { - right: 0; left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) { - transform: none; - } - .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show { - visibility: visible; - } -} -@media (min-width: 1400px) { - .offcanvas-xxl { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-xxl .offcanvas-header { - display: none; - } - .offcanvas-xxl .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } -} - -.offcanvas { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); -} -@media (prefers-reduced-motion: reduce) { - .offcanvas { - transition: none; - } -} -.offcanvas.offcanvas-start { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); -} -.offcanvas.offcanvas-end { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); -} -.offcanvas.offcanvas-top { - top: 0; - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); -} -.offcanvas.offcanvas-bottom { - right: 0; - left: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); -} -.offcanvas.showing, .offcanvas.show:not(.hiding) { - transform: none; -} -.offcanvas.showing, .offcanvas.hiding, .offcanvas.show { - visibility: visible; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; } -.offcanvas-backdrop { - position: fixed; - top: 0; - left: 0; - z-index: 1040; - width: 100vw; - height: 100vh; - background-color: #000; -} .offcanvas-backdrop.fade { - opacity: 0; + opacity: 0; } + .offcanvas-backdrop.show { - opacity: 0.5; + opacity: 0.5; } .offcanvas-header { - display: flex; - align-items: center; - padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); + display: flex; + align-items: center; + padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); } + .offcanvas-header .btn-close { - padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5); - margin: calc(-0.5 * var(--bs-offcanvas-padding-y)) calc(-0.5 * var(--bs-offcanvas-padding-x)) calc(-0.5 * var(--bs-offcanvas-padding-y)) auto; + padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5); + margin: calc(-0.5 * var(--bs-offcanvas-padding-y)) calc(-0.5 * var(--bs-offcanvas-padding-x)) calc(-0.5 * var(--bs-offcanvas-padding-y)) auto; } .offcanvas-title { - margin-bottom: 0; - line-height: var(--bs-offcanvas-title-line-height); + margin-bottom: 0; + line-height: var(--bs-offcanvas-title-line-height); } .offcanvas-body { - flex-grow: 1; - padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); - overflow-y: auto; + flex-grow: 1; + padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); + overflow-y: auto; } .placeholder { - display: inline-block; - min-height: 1em; - vertical-align: middle; - cursor: wait; - background-color: currentcolor; - opacity: 0.5; + display: inline-block; + min-height: 1em; + vertical-align: middle; + cursor: wait; + background-color: currentcolor; + opacity: 0.5; } + .placeholder.btn::before { - display: inline-block; - content: ""; + display: inline-block; + content: ""; } .placeholder-xs { - min-height: 0.6em; + min-height: 0.6em; } .placeholder-sm { - min-height: 0.8em; + min-height: 0.8em; } .placeholder-lg { - min-height: 1.2em; + min-height: 1.2em; } .placeholder-glow .placeholder { - animation: placeholder-glow 2s ease-in-out infinite; + animation: placeholder-glow 2s ease-in-out infinite; } @keyframes placeholder-glow { - 50% { - opacity: 0.2; - } + 50% { + opacity: 0.2; + } } + .placeholder-wave { - -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); - mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); - -webkit-mask-size: 200% 100%; - mask-size: 200% 100%; - animation: placeholder-wave 2s linear infinite; + -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); + mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); + -webkit-mask-size: 200% 100%; + mask-size: 200% 100%; + animation: placeholder-wave 2s linear infinite; } @keyframes placeholder-wave { - 100% { - -webkit-mask-position: -200% 0%; - mask-position: -200% 0%; - } + 100% { + -webkit-mask-position: -200% 0%; + mask-position: -200% 0%; + } } + .clearfix::after { - display: block; - clear: both; - content: ""; + display: block; + clear: both; + content: ""; } .text-bg-primary { - color: #fff !important; - background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-secondary { - color: #fff !important; - background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-success { - color: #fff !important; - background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-info { - color: #000 !important; - background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-warning { - color: #000 !important; - background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-danger { - color: #fff !important; - background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-light { - color: #000 !important; - background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-dark { - color: #fff !important; - background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; } .link-primary { - color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-primary:hover, .link-primary:focus { - color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; } .link-secondary { - color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-secondary:hover, .link-secondary:focus { - color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; } .link-success { - color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-success:hover, .link-success:focus { - color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; } .link-info { - color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-info:hover, .link-info:focus { - color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; } .link-warning { - color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-warning:hover, .link-warning:focus { - color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; } .link-danger { - color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-danger:hover, .link-danger:focus { - color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; } .link-light { - color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-light:hover, .link-light:focus { - color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; } .link-dark { - color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-dark:hover, .link-dark:focus { - color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; } .link-body-emphasis { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-body-emphasis:hover, .link-body-emphasis:focus { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; } .focus-ring:focus { - outline: 0; - box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); + outline: 0; + box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); } .icon-link { - display: inline-flex; - gap: 0.375rem; - align-items: center; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-underline-offset: 0.25em; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; + display: inline-flex; + gap: 0.375rem; + align-items: center; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-underline-offset: 0.25em; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + .icon-link > .bi { - flex-shrink: 0; - width: 1em; - height: 1em; - fill: currentcolor; - transition: 0.2s ease-in-out transform; + flex-shrink: 0; + width: 1em; + height: 1em; + fill: currentcolor; + transition: 0.2s ease-in-out transform; } + @media (prefers-reduced-motion: reduce) { - .icon-link > .bi { - transition: none; - } + .icon-link > .bi { + transition: none; + } } .icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi { - transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); + transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); } .ratio { - position: relative; - width: 100%; -} -.ratio::before { - display: block; - padding-top: var(--bs-aspect-ratio); - content: ""; -} -.ratio > * { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; + position: relative; + width: 100%; } -.ratio-1x1 { - --bs-aspect-ratio: 100%; +.ratio::before { + display: block; + padding-top: var(--bs-aspect-ratio); + content: ""; +} + +.ratio > * { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.ratio-1x1 { + --bs-aspect-ratio: 100%; } .ratio-4x3 { - --bs-aspect-ratio: 75%; + --bs-aspect-ratio: 75%; } .ratio-16x9 { - --bs-aspect-ratio: 56.25%; + --bs-aspect-ratio: 56.25%; } .ratio-21x9 { - --bs-aspect-ratio: 42.8571428571%; + --bs-aspect-ratio: 42.8571428571%; } .fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1030; + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; } .fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 1030; + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; } .sticky-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; -} - -.sticky-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; -} - -@media (min-width: 576px) { - .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; - } - .sticky-sm-bottom { +} + +.sticky-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; - } } + +@media (min-width: 576px) { + .sticky-sm-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-sm-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } +} + @media (min-width: 768px) { - .sticky-md-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-md-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-md-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-md-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 992px) { - .sticky-lg-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-lg-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-lg-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-lg-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1200px) { - .sticky-xl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1400px) { - .sticky-xxl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xxl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xxl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xxl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + .hstack { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; + display: flex; + flex-direction: row; + align-items: center; + align-self: stretch; } .vstack { - display: flex; - flex: 1 1 auto; - flex-direction: column; - align-self: stretch; + display: flex; + flex: 1 1 auto; + flex-direction: column; + align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; } + .visually-hidden:not(caption), .visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) { - position: absolute !important; + position: absolute !important; } .stretched-link::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + content: ""; } .text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .vr { - display: inline-block; - align-self: stretch; - width: var(--bs-border-width); - min-height: 1em; - background-color: currentcolor; - opacity: 0.25; + display: inline-block; + align-self: stretch; + width: var(--bs-border-width); + min-height: 1em; + background-color: currentcolor; + opacity: 0.25; } .align-baseline { - vertical-align: baseline !important; + vertical-align: baseline !important; } .align-top { - vertical-align: top !important; + vertical-align: top !important; } .align-middle { - vertical-align: middle !important; + vertical-align: middle !important; } .align-bottom { - vertical-align: bottom !important; + vertical-align: bottom !important; } .align-text-bottom { - vertical-align: text-bottom !important; + vertical-align: text-bottom !important; } .align-text-top { - vertical-align: text-top !important; + vertical-align: text-top !important; } .float-start { - float: left !important; + float: left !important; } .float-end { - float: right !important; + float: right !important; } .float-none { - float: none !important; + float: none !important; } .object-fit-contain { - -o-object-fit: contain !important; - object-fit: contain !important; + -o-object-fit: contain !important; + object-fit: contain !important; } .object-fit-cover { - -o-object-fit: cover !important; - object-fit: cover !important; + -o-object-fit: cover !important; + object-fit: cover !important; } .object-fit-fill { - -o-object-fit: fill !important; - object-fit: fill !important; + -o-object-fit: fill !important; + object-fit: fill !important; } .object-fit-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; + -o-object-fit: scale-down !important; + object-fit: scale-down !important; } .object-fit-none { - -o-object-fit: none !important; - object-fit: none !important; + -o-object-fit: none !important; + object-fit: none !important; } .opacity-0 { - opacity: 0 !important; + opacity: 0 !important; } .opacity-25 { - opacity: 0.25 !important; + opacity: 0.25 !important; } .opacity-50 { - opacity: 0.5 !important; + opacity: 0.5 !important; } .opacity-75 { - opacity: 0.75 !important; + opacity: 0.75 !important; } .opacity-100 { - opacity: 1 !important; + opacity: 1 !important; } .overflow-auto { - overflow: auto !important; + overflow: auto !important; } .overflow-hidden { - overflow: hidden !important; + overflow: hidden !important; } .overflow-visible { - overflow: visible !important; + overflow: visible !important; } .overflow-scroll { - overflow: scroll !important; + overflow: scroll !important; } .overflow-x-auto { - overflow-x: auto !important; + overflow-x: auto !important; } .overflow-x-hidden { - overflow-x: hidden !important; + overflow-x: hidden !important; } .overflow-x-visible { - overflow-x: visible !important; + overflow-x: visible !important; } .overflow-x-scroll { - overflow-x: scroll !important; + overflow-x: scroll !important; } .overflow-y-auto { - overflow-y: auto !important; + overflow-y: auto !important; } .overflow-y-hidden { - overflow-y: hidden !important; + overflow-y: hidden !important; } .overflow-y-visible { - overflow-y: visible !important; + overflow-y: visible !important; } .overflow-y-scroll { - overflow-y: scroll !important; + overflow-y: scroll !important; } .d-inline { - display: inline !important; + display: inline !important; } .d-inline-block { - display: inline-block !important; + display: inline-block !important; } .d-block { - display: block !important; + display: block !important; } .d-grid { - display: grid !important; + display: grid !important; } .d-inline-grid { - display: inline-grid !important; + display: inline-grid !important; } .d-table { - display: table !important; + display: table !important; } .d-table-row { - display: table-row !important; + display: table-row !important; } .d-table-cell { - display: table-cell !important; + display: table-cell !important; } .d-flex { - display: flex !important; + display: flex !important; } .d-inline-flex { - display: inline-flex !important; + display: inline-flex !important; } .d-none { - display: none !important; + display: none !important; } .shadow { - box-shadow: var(--bs-box-shadow) !important; + box-shadow: var(--bs-box-shadow) !important; } .shadow-sm { - box-shadow: var(--bs-box-shadow-sm) !important; + box-shadow: var(--bs-box-shadow-sm) !important; } .shadow-lg { - box-shadow: var(--bs-box-shadow-lg) !important; + box-shadow: var(--bs-box-shadow-lg) !important; } .shadow-none { - box-shadow: none !important; + box-shadow: none !important; } .focus-ring-primary { - --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-secondary { - --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-success { - --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-info { - --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-warning { - --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-danger { - --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-light { - --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-dark { - --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); } .position-static { - position: static !important; + position: static !important; } .position-relative { - position: relative !important; + position: relative !important; } .position-absolute { - position: absolute !important; + position: absolute !important; } .position-fixed { - position: fixed !important; + position: fixed !important; } .position-sticky { - position: -webkit-sticky !important; - position: sticky !important; + position: -webkit-sticky !important; + position: sticky !important; } .top-0 { - top: 0 !important; + top: 0 !important; } .top-50 { - top: 50% !important; + top: 50% !important; } .top-100 { - top: 100% !important; + top: 100% !important; } .bottom-0 { - bottom: 0 !important; + bottom: 0 !important; } .bottom-50 { - bottom: 50% !important; + bottom: 50% !important; } .bottom-100 { - bottom: 100% !important; + bottom: 100% !important; } .start-0 { - left: 0 !important; + left: 0 !important; } .start-50 { - left: 50% !important; + left: 50% !important; } .start-100 { - left: 100% !important; + left: 100% !important; } .end-0 { - right: 0 !important; + right: 0 !important; } .end-50 { - right: 50% !important; + right: 50% !important; } .end-100 { - right: 100% !important; + right: 100% !important; } .translate-middle { - transform: translate(-50%, -50%) !important; + transform: translate(-50%, -50%) !important; } .translate-middle-x { - transform: translateX(-50%) !important; + transform: translateX(-50%) !important; } .translate-middle-y { - transform: translateY(-50%) !important; + transform: translateY(-50%) !important; } .border { - border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-0 { - border: 0 !important; + border: 0 !important; } .border-top { - border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-top-0 { - border-top: 0 !important; + border-top: 0 !important; } .border-end { - border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-end-0 { - border-right: 0 !important; + border-right: 0 !important; } .border-bottom { - border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-bottom-0 { - border-bottom: 0 !important; + border-bottom: 0 !important; } .border-start { - border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-start-0 { - border-left: 0 !important; + border-left: 0 !important; } .border-primary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; } .border-secondary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; } .border-success { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; } .border-info { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; } .border-warning { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; } .border-danger { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; } .border-light { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; } .border-dark { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } .border-black { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; } .border-white { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } .border-primary-subtle { - border-color: var(--bs-primary-border-subtle) !important; + border-color: var(--bs-primary-border-subtle) !important; } .border-secondary-subtle { - border-color: var(--bs-secondary-border-subtle) !important; + border-color: var(--bs-secondary-border-subtle) !important; } .border-success-subtle { - border-color: var(--bs-success-border-subtle) !important; + border-color: var(--bs-success-border-subtle) !important; } .border-info-subtle { - border-color: var(--bs-info-border-subtle) !important; + border-color: var(--bs-info-border-subtle) !important; } .border-warning-subtle { - border-color: var(--bs-warning-border-subtle) !important; + border-color: var(--bs-warning-border-subtle) !important; } .border-danger-subtle { - border-color: var(--bs-danger-border-subtle) !important; + border-color: var(--bs-danger-border-subtle) !important; } .border-light-subtle { - border-color: var(--bs-light-border-subtle) !important; + border-color: var(--bs-light-border-subtle) !important; } .border-dark-subtle { - border-color: var(--bs-dark-border-subtle) !important; + border-color: var(--bs-dark-border-subtle) !important; } .border-1 { - border-width: 1px !important; + border-width: 1px !important; } .border-2 { - border-width: 2px !important; + border-width: 2px !important; } .border-3 { - border-width: 3px !important; + border-width: 3px !important; } .border-4 { - border-width: 4px !important; + border-width: 4px !important; } .border-5 { - border-width: 5px !important; + border-width: 5px !important; } .border-opacity-10 { - --bs-border-opacity: 0.1; + --bs-border-opacity: 0.1; } .border-opacity-25 { - --bs-border-opacity: 0.25; + --bs-border-opacity: 0.25; } .border-opacity-50 { - --bs-border-opacity: 0.5; + --bs-border-opacity: 0.5; } .border-opacity-75 { - --bs-border-opacity: 0.75; + --bs-border-opacity: 0.75; } .border-opacity-100 { - --bs-border-opacity: 1; + --bs-border-opacity: 1; } .w-25 { - width: 25% !important; + width: 25% !important; } .w-50 { - width: 50% !important; + width: 50% !important; } .w-75 { - width: 75% !important; + width: 75% !important; } .w-100 { - width: 100% !important; + width: 100% !important; } .w-auto { - width: auto !important; + width: auto !important; } .mw-100 { - max-width: 100% !important; + max-width: 100% !important; } .vw-100 { - width: 100vw !important; + width: 100vw !important; } .min-vw-100 { - min-width: 100vw !important; + min-width: 100vw !important; } .h-25 { - height: 25% !important; + height: 25% !important; } .h-50 { - height: 50% !important; + height: 50% !important; } .h-75 { - height: 75% !important; + height: 75% !important; } .h-100 { - height: 100% !important; + height: 100% !important; } .h-auto { - height: auto !important; + height: auto !important; } .mh-100 { - max-height: 100% !important; + max-height: 100% !important; } .vh-100 { - height: 100vh !important; + height: 100vh !important; } .min-vh-100 { - min-height: 100vh !important; + min-height: 100vh !important; } .flex-fill { - flex: 1 1 auto !important; + flex: 1 1 auto !important; } .flex-row { - flex-direction: row !important; + flex-direction: row !important; } .flex-column { - flex-direction: column !important; + flex-direction: column !important; } .flex-row-reverse { - flex-direction: row-reverse !important; + flex-direction: row-reverse !important; } .flex-column-reverse { - flex-direction: column-reverse !important; + flex-direction: column-reverse !important; } .flex-grow-0 { - flex-grow: 0 !important; + flex-grow: 0 !important; } .flex-grow-1 { - flex-grow: 1 !important; + flex-grow: 1 !important; } .flex-shrink-0 { - flex-shrink: 0 !important; + flex-shrink: 0 !important; } .flex-shrink-1 { - flex-shrink: 1 !important; + flex-shrink: 1 !important; } .flex-wrap { - flex-wrap: wrap !important; + flex-wrap: wrap !important; } .flex-nowrap { - flex-wrap: nowrap !important; + flex-wrap: nowrap !important; } .flex-wrap-reverse { - flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; } .justify-content-start { - justify-content: flex-start !important; + justify-content: flex-start !important; } .justify-content-end { - justify-content: flex-end !important; + justify-content: flex-end !important; } .justify-content-center { - justify-content: center !important; + justify-content: center !important; } .justify-content-between { - justify-content: space-between !important; + justify-content: space-between !important; } .justify-content-around { - justify-content: space-around !important; + justify-content: space-around !important; } .justify-content-evenly { - justify-content: space-evenly !important; + justify-content: space-evenly !important; } .align-items-start { - align-items: flex-start !important; + align-items: flex-start !important; } .align-items-end { - align-items: flex-end !important; + align-items: flex-end !important; } .align-items-center { - align-items: center !important; + align-items: center !important; } .align-items-baseline { - align-items: baseline !important; + align-items: baseline !important; } .align-items-stretch { - align-items: stretch !important; + align-items: stretch !important; } .align-content-start { - align-content: flex-start !important; + align-content: flex-start !important; } .align-content-end { - align-content: flex-end !important; + align-content: flex-end !important; } .align-content-center { - align-content: center !important; + align-content: center !important; } .align-content-between { - align-content: space-between !important; + align-content: space-between !important; } .align-content-around { - align-content: space-around !important; + align-content: space-around !important; } .align-content-stretch { - align-content: stretch !important; + align-content: stretch !important; } .align-self-auto { - align-self: auto !important; + align-self: auto !important; } .align-self-start { - align-self: flex-start !important; + align-self: flex-start !important; } .align-self-end { - align-self: flex-end !important; + align-self: flex-end !important; } .align-self-center { - align-self: center !important; + align-self: center !important; } .align-self-baseline { - align-self: baseline !important; + align-self: baseline !important; } .align-self-stretch { - align-self: stretch !important; + align-self: stretch !important; } .order-first { - order: -1 !important; + order: -1 !important; } .order-0 { - order: 0 !important; + order: 0 !important; } .order-1 { - order: 1 !important; + order: 1 !important; } .order-2 { - order: 2 !important; + order: 2 !important; } .order-3 { - order: 3 !important; + order: 3 !important; } .order-4 { - order: 4 !important; + order: 4 !important; } .order-5 { - order: 5 !important; + order: 5 !important; } .order-last { - order: 6 !important; + order: 6 !important; } .m-0 { - margin: 0 !important; + margin: 0 !important; } .m-1 { - margin: 0.25rem !important; + margin: 0.25rem !important; } .m-2 { - margin: 0.5rem !important; + margin: 0.5rem !important; } .m-3 { - margin: 1rem !important; + margin: 1rem !important; } .m-4 { - margin: 1.5rem !important; + margin: 1.5rem !important; } .m-5 { - margin: 3rem !important; + margin: 3rem !important; } .m-auto { - margin: auto !important; + margin: auto !important; } .mx-0 { - margin-right: 0 !important; - margin-left: 0 !important; + margin-right: 0 !important; + margin-left: 0 !important; } .mx-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } .mx-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } .mx-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; + margin-right: 1rem !important; + margin-left: 1rem !important; } .mx-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } .mx-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; + margin-right: 3rem !important; + margin-left: 3rem !important; } .mx-auto { - margin-right: auto !important; - margin-left: auto !important; + margin-right: auto !important; + margin-left: auto !important; } .my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; } .my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } .my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } .my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; + margin-top: 1rem !important; + margin-bottom: 1rem !important; } .my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } .my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; + margin-top: 3rem !important; + margin-bottom: 3rem !important; } .my-auto { - margin-top: auto !important; - margin-bottom: auto !important; + margin-top: auto !important; + margin-bottom: auto !important; } .mt-0 { - margin-top: 0 !important; + margin-top: 0 !important; } .mt-1 { - margin-top: 0.25rem !important; + margin-top: 0.25rem !important; } .mt-2 { - margin-top: 0.5rem !important; + margin-top: 0.5rem !important; } .mt-3 { - margin-top: 1rem !important; + margin-top: 1rem !important; } .mt-4 { - margin-top: 1.5rem !important; + margin-top: 1.5rem !important; } .mt-5 { - margin-top: 3rem !important; + margin-top: 3rem !important; } .mt-auto { - margin-top: auto !important; + margin-top: auto !important; } .me-0 { - margin-right: 0 !important; + margin-right: 0 !important; } .me-1 { - margin-right: 0.25rem !important; + margin-right: 0.25rem !important; } .me-2 { - margin-right: 0.5rem !important; + margin-right: 0.5rem !important; } .me-3 { - margin-right: 1rem !important; + margin-right: 1rem !important; } .me-4 { - margin-right: 1.5rem !important; + margin-right: 1.5rem !important; } .me-5 { - margin-right: 3rem !important; + margin-right: 3rem !important; } .me-auto { - margin-right: auto !important; + margin-right: auto !important; } .mb-0 { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .mb-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 0.25rem !important; } .mb-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 0.5rem !important; } .mb-3 { - margin-bottom: 1rem !important; + margin-bottom: 1rem !important; } .mb-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 1.5rem !important; } .mb-5 { - margin-bottom: 3rem !important; + margin-bottom: 3rem !important; } .mb-auto { - margin-bottom: auto !important; + margin-bottom: auto !important; } .ms-0 { - margin-left: 0 !important; + margin-left: 0 !important; } .ms-1 { - margin-left: 0.25rem !important; + margin-left: 0.25rem !important; } .ms-2 { - margin-left: 0.5rem !important; + margin-left: 0.5rem !important; } .ms-3 { - margin-left: 1rem !important; + margin-left: 1rem !important; } .ms-4 { - margin-left: 1.5rem !important; + margin-left: 1.5rem !important; } .ms-5 { - margin-left: 3rem !important; + margin-left: 3rem !important; } .ms-auto { - margin-left: auto !important; + margin-left: auto !important; } .p-0 { - padding: 0 !important; + padding: 0 !important; } .p-1 { - padding: 0.25rem !important; + padding: 0.25rem !important; } .p-2 { - padding: 0.5rem !important; + padding: 0.5rem !important; } .p-3 { - padding: 1rem !important; + padding: 1rem !important; } .p-4 { - padding: 1.5rem !important; + padding: 1.5rem !important; } .p-5 { - padding: 3rem !important; + padding: 3rem !important; } .px-0 { - padding-right: 0 !important; - padding-left: 0 !important; + padding-right: 0 !important; + padding-left: 0 !important; } .px-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } .px-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } .px-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; + padding-right: 1rem !important; + padding-left: 1rem !important; } .px-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } .px-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; + padding-right: 3rem !important; + padding-left: 3rem !important; } .py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; } .py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } .py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } .py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; + padding-top: 1rem !important; + padding-bottom: 1rem !important; } .py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } .py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; + padding-top: 3rem !important; + padding-bottom: 3rem !important; } .pt-0 { - padding-top: 0 !important; + padding-top: 0 !important; } .pt-1 { - padding-top: 0.25rem !important; + padding-top: 0.25rem !important; } .pt-2 { - padding-top: 0.5rem !important; + padding-top: 0.5rem !important; } .pt-3 { - padding-top: 1rem !important; + padding-top: 1rem !important; } .pt-4 { - padding-top: 1.5rem !important; + padding-top: 1.5rem !important; } .pt-5 { - padding-top: 3rem !important; + padding-top: 3rem !important; } .pe-0 { - padding-right: 0 !important; + padding-right: 0 !important; } .pe-1 { - padding-right: 0.25rem !important; + padding-right: 0.25rem !important; } .pe-2 { - padding-right: 0.5rem !important; + padding-right: 0.5rem !important; } .pe-3 { - padding-right: 1rem !important; + padding-right: 1rem !important; } .pe-4 { - padding-right: 1.5rem !important; + padding-right: 1.5rem !important; } .pe-5 { - padding-right: 3rem !important; + padding-right: 3rem !important; } .pb-0 { - padding-bottom: 0 !important; + padding-bottom: 0 !important; } .pb-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 0.25rem !important; } .pb-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 0.5rem !important; } .pb-3 { - padding-bottom: 1rem !important; + padding-bottom: 1rem !important; } .pb-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 1.5rem !important; } .pb-5 { - padding-bottom: 3rem !important; + padding-bottom: 3rem !important; } .ps-0 { - padding-left: 0 !important; + padding-left: 0 !important; } .ps-1 { - padding-left: 0.25rem !important; + padding-left: 0.25rem !important; } .ps-2 { - padding-left: 0.5rem !important; + padding-left: 0.5rem !important; } .ps-3 { - padding-left: 1rem !important; + padding-left: 1rem !important; } .ps-4 { - padding-left: 1.5rem !important; + padding-left: 1.5rem !important; } .ps-5 { - padding-left: 3rem !important; + padding-left: 3rem !important; } .gap-0 { - gap: 0 !important; + gap: 0 !important; } .gap-1 { - gap: 0.25rem !important; + gap: 0.25rem !important; } .gap-2 { - gap: 0.5rem !important; + gap: 0.5rem !important; } .gap-3 { - gap: 1rem !important; + gap: 1rem !important; } .gap-4 { - gap: 1.5rem !important; + gap: 1.5rem !important; } .gap-5 { - gap: 3rem !important; + gap: 3rem !important; } .row-gap-0 { - row-gap: 0 !important; + row-gap: 0 !important; } .row-gap-1 { - row-gap: 0.25rem !important; + row-gap: 0.25rem !important; } .row-gap-2 { - row-gap: 0.5rem !important; + row-gap: 0.5rem !important; } .row-gap-3 { - row-gap: 1rem !important; + row-gap: 1rem !important; } .row-gap-4 { - row-gap: 1.5rem !important; + row-gap: 1.5rem !important; } .row-gap-5 { - row-gap: 3rem !important; + row-gap: 3rem !important; } .column-gap-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; + -moz-column-gap: 0 !important; + column-gap: 0 !important; } .column-gap-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; } .column-gap-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; } .column-gap-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; } .column-gap-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; } .column-gap-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; } .font-monospace { - font-family: var(--bs-font-monospace) !important; + font-family: var(--bs-font-monospace) !important; } .fs-1 { - font-size: calc(1.375rem + 1.5vw) !important; + font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { - font-size: calc(1.325rem + 0.9vw) !important; + font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { - font-size: calc(1.3rem + 0.6vw) !important; + font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { - font-size: calc(1.275rem + 0.3vw) !important; + font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { - font-size: 1.25rem !important; + font-size: 1.25rem !important; } .fs-6 { - font-size: 1rem !important; + font-size: 1rem !important; } .fst-italic { - font-style: italic !important; + font-style: italic !important; } .fst-normal { - font-style: normal !important; + font-style: normal !important; } .fw-lighter { - font-weight: lighter !important; + font-weight: lighter !important; } .fw-light { - font-weight: 300 !important; + font-weight: 300 !important; } .fw-normal { - font-weight: 400 !important; + font-weight: 400 !important; } .fw-medium { - font-weight: 500 !important; + font-weight: 500 !important; } .fw-semibold { - font-weight: 600 !important; + font-weight: 600 !important; } .fw-bold { - font-weight: 700 !important; + font-weight: 700 !important; } .fw-bolder { - font-weight: bolder !important; + font-weight: bolder !important; } .lh-1 { - line-height: 1 !important; + line-height: 1 !important; } .lh-sm { - line-height: 1.25 !important; + line-height: 1.25 !important; } .lh-base { - line-height: 1.5 !important; + line-height: 1.5 !important; } .lh-lg { - line-height: 2 !important; + line-height: 2 !important; } .text-start { - text-align: left !important; + text-align: left !important; } .text-end { - text-align: right !important; + text-align: right !important; } .text-center { - text-align: center !important; + text-align: center !important; } .text-decoration-none { - text-decoration: none !important; + text-decoration: none !important; } .text-decoration-underline { - text-decoration: underline !important; + text-decoration: underline !important; } .text-decoration-line-through { - text-decoration: line-through !important; + text-decoration: line-through !important; } .text-lowercase { - text-transform: lowercase !important; + text-transform: lowercase !important; } .text-uppercase { - text-transform: uppercase !important; + text-transform: uppercase !important; } .text-capitalize { - text-transform: capitalize !important; + text-transform: capitalize !important; } .text-wrap { - white-space: normal !important; + white-space: normal !important; } .text-nowrap { - white-space: nowrap !important; + white-space: nowrap !important; } /* rtl:begin:remove */ .text-break { - word-wrap: break-word !important; - word-break: break-word !important; + word-wrap: break-word !important; + word-break: break-word !important; } /* rtl:end:remove */ .text-primary { - --bs-text-opacity: 1; - color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { - --bs-text-opacity: 1; - color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { - --bs-text-opacity: 1; - color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { - --bs-text-opacity: 1; - color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { - --bs-text-opacity: 1; - color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { - --bs-text-opacity: 1; - color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { - --bs-text-opacity: 1; - color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { - --bs-text-opacity: 1; - color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { - --bs-text-opacity: 1; - color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { - --bs-text-opacity: 1; - color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { - --bs-text-opacity: 1; - color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-black-50 { - --bs-text-opacity: 1; - color: rgba(0, 0, 0, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { - --bs-text-opacity: 1; - color: rgba(255, 255, 255, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(255, 255, 255, 0.5) !important; } .text-body-secondary { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-body-tertiary { - --bs-text-opacity: 1; - color: var(--bs-tertiary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-tertiary-color) !important; } .text-body-emphasis { - --bs-text-opacity: 1; - color: var(--bs-emphasis-color) !important; + --bs-text-opacity: 1; + color: var(--bs-emphasis-color) !important; } .text-reset { - --bs-text-opacity: 1; - color: inherit !important; + --bs-text-opacity: 1; + color: inherit !important; } .text-opacity-25 { - --bs-text-opacity: 0.25; + --bs-text-opacity: 0.25; } .text-opacity-50 { - --bs-text-opacity: 0.5; + --bs-text-opacity: 0.5; } .text-opacity-75 { - --bs-text-opacity: 0.75; + --bs-text-opacity: 0.75; } .text-opacity-100 { - --bs-text-opacity: 1; + --bs-text-opacity: 1; } .text-primary-emphasis { - color: var(--bs-primary-text-emphasis) !important; + color: var(--bs-primary-text-emphasis) !important; } .text-secondary-emphasis { - color: var(--bs-secondary-text-emphasis) !important; + color: var(--bs-secondary-text-emphasis) !important; } .text-success-emphasis { - color: var(--bs-success-text-emphasis) !important; + color: var(--bs-success-text-emphasis) !important; } .text-info-emphasis { - color: var(--bs-info-text-emphasis) !important; + color: var(--bs-info-text-emphasis) !important; } .text-warning-emphasis { - color: var(--bs-warning-text-emphasis) !important; + color: var(--bs-warning-text-emphasis) !important; } .text-danger-emphasis { - color: var(--bs-danger-text-emphasis) !important; + color: var(--bs-danger-text-emphasis) !important; } .text-light-emphasis { - color: var(--bs-light-text-emphasis) !important; + color: var(--bs-light-text-emphasis) !important; } .text-dark-emphasis { - color: var(--bs-dark-text-emphasis) !important; + color: var(--bs-dark-text-emphasis) !important; } .link-opacity-10 { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-10-hover:hover { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-25 { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-25-hover:hover { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-50 { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-50-hover:hover { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-75 { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-75-hover:hover { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-100 { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-opacity-100-hover:hover { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-offset-1 { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-1-hover:hover { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-2 { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-2-hover:hover { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-3 { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-offset-3-hover:hover { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-underline-primary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-secondary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-success { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-info { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-warning { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-danger { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-light { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-dark { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-underline-opacity-0 { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-0-hover:hover { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-10 { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-10-hover:hover { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-25 { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-25-hover:hover { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-50 { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-50-hover:hover { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-75 { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-75-hover:hover { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-100 { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .link-underline-opacity-100-hover:hover { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .bg-primary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; } .bg-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; } .bg-success { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; } .bg-info { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; } .bg-danger { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-transparent { - --bs-bg-opacity: 1; - background-color: transparent !important; + --bs-bg-opacity: 1; + background-color: transparent !important; } .bg-body-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-body-tertiary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-opacity-10 { - --bs-bg-opacity: 0.1; + --bs-bg-opacity: 0.1; } .bg-opacity-25 { - --bs-bg-opacity: 0.25; + --bs-bg-opacity: 0.25; } .bg-opacity-50 { - --bs-bg-opacity: 0.5; + --bs-bg-opacity: 0.5; } .bg-opacity-75 { - --bs-bg-opacity: 0.75; + --bs-bg-opacity: 0.75; } .bg-opacity-100 { - --bs-bg-opacity: 1; + --bs-bg-opacity: 1; } .bg-primary-subtle { - background-color: var(--bs-primary-bg-subtle) !important; + background-color: var(--bs-primary-bg-subtle) !important; } .bg-secondary-subtle { - background-color: var(--bs-secondary-bg-subtle) !important; + background-color: var(--bs-secondary-bg-subtle) !important; } .bg-success-subtle { - background-color: var(--bs-success-bg-subtle) !important; + background-color: var(--bs-success-bg-subtle) !important; } .bg-info-subtle { - background-color: var(--bs-info-bg-subtle) !important; + background-color: var(--bs-info-bg-subtle) !important; } .bg-warning-subtle { - background-color: var(--bs-warning-bg-subtle) !important; + background-color: var(--bs-warning-bg-subtle) !important; } .bg-danger-subtle { - background-color: var(--bs-danger-bg-subtle) !important; + background-color: var(--bs-danger-bg-subtle) !important; } .bg-light-subtle { - background-color: var(--bs-light-bg-subtle) !important; + background-color: var(--bs-light-bg-subtle) !important; } .bg-dark-subtle { - background-color: var(--bs-dark-bg-subtle) !important; + background-color: var(--bs-dark-bg-subtle) !important; } .bg-gradient { - background-image: var(--bs-gradient) !important; + background-image: var(--bs-gradient) !important; } .user-select-all { - -webkit-user-select: all !important; - -moz-user-select: all !important; - user-select: all !important; + -webkit-user-select: all !important; + -moz-user-select: all !important; + user-select: all !important; } .user-select-auto { - -webkit-user-select: auto !important; - -moz-user-select: auto !important; - user-select: auto !important; + -webkit-user-select: auto !important; + -moz-user-select: auto !important; + user-select: auto !important; } .user-select-none { - -webkit-user-select: none !important; - -moz-user-select: none !important; - user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + user-select: none !important; } .pe-none { - pointer-events: none !important; + pointer-events: none !important; } .pe-auto { - pointer-events: auto !important; + pointer-events: auto !important; } .rounded { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-0 { - border-radius: 0 !important; + border-radius: 0 !important; } .rounded-1 { - border-radius: var(--bs-border-radius-sm) !important; + border-radius: var(--bs-border-radius-sm) !important; } .rounded-2 { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-3 { - border-radius: var(--bs-border-radius-lg) !important; + border-radius: var(--bs-border-radius-lg) !important; } .rounded-4 { - border-radius: var(--bs-border-radius-xl) !important; + border-radius: var(--bs-border-radius-xl) !important; } .rounded-5 { - border-radius: var(--bs-border-radius-xxl) !important; + border-radius: var(--bs-border-radius-xxl) !important; } .rounded-circle { - border-radius: 50% !important; + border-radius: 50% !important; } .rounded-pill { - border-radius: var(--bs-border-radius-pill) !important; + border-radius: var(--bs-border-radius-pill) !important; } .rounded-top { - border-top-left-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-top-0 { - border-top-left-radius: 0 !important; - border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; } .rounded-top-1 { - border-top-left-radius: var(--bs-border-radius-sm) !important; - border-top-right-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; } .rounded-top-2 { - border-top-left-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-top-3 { - border-top-left-radius: var(--bs-border-radius-lg) !important; - border-top-right-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; } .rounded-top-4 { - border-top-left-radius: var(--bs-border-radius-xl) !important; - border-top-right-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; } .rounded-top-5 { - border-top-left-radius: var(--bs-border-radius-xxl) !important; - border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-top-circle { - border-top-left-radius: 50% !important; - border-top-right-radius: 50% !important; + border-top-left-radius: 50% !important; + border-top-right-radius: 50% !important; } .rounded-top-pill { - border-top-left-radius: var(--bs-border-radius-pill) !important; - border-top-right-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; } .rounded-end { - border-top-right-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-end-0 { - border-top-right-radius: 0 !important; - border-bottom-right-radius: 0 !important; + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; } .rounded-end-1 { - border-top-right-radius: var(--bs-border-radius-sm) !important; - border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; } .rounded-end-2 { - border-top-right-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-end-3 { - border-top-right-radius: var(--bs-border-radius-lg) !important; - border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; } .rounded-end-4 { - border-top-right-radius: var(--bs-border-radius-xl) !important; - border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; } .rounded-end-5 { - border-top-right-radius: var(--bs-border-radius-xxl) !important; - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-end-circle { - border-top-right-radius: 50% !important; - border-bottom-right-radius: 50% !important; + border-top-right-radius: 50% !important; + border-bottom-right-radius: 50% !important; } .rounded-end-pill { - border-top-right-radius: var(--bs-border-radius-pill) !important; - border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; } .rounded-bottom { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-bottom-0 { - border-bottom-right-radius: 0 !important; - border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important; } .rounded-bottom-1 { - border-bottom-right-radius: var(--bs-border-radius-sm) !important; - border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; } .rounded-bottom-2 { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-bottom-3 { - border-bottom-right-radius: var(--bs-border-radius-lg) !important; - border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; } .rounded-bottom-4 { - border-bottom-right-radius: var(--bs-border-radius-xl) !important; - border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; } .rounded-bottom-5 { - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-bottom-circle { - border-bottom-right-radius: 50% !important; - border-bottom-left-radius: 50% !important; + border-bottom-right-radius: 50% !important; + border-bottom-left-radius: 50% !important; } .rounded-bottom-pill { - border-bottom-right-radius: var(--bs-border-radius-pill) !important; - border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; } .rounded-start { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-start-0 { - border-bottom-left-radius: 0 !important; - border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important; } .rounded-start-1 { - border-bottom-left-radius: var(--bs-border-radius-sm) !important; - border-top-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; } .rounded-start-2 { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-start-3 { - border-bottom-left-radius: var(--bs-border-radius-lg) !important; - border-top-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; } .rounded-start-4 { - border-bottom-left-radius: var(--bs-border-radius-xl) !important; - border-top-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; } .rounded-start-5 { - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; - border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-start-circle { - border-bottom-left-radius: 50% !important; - border-top-left-radius: 50% !important; + border-bottom-left-radius: 50% !important; + border-top-left-radius: 50% !important; } .rounded-start-pill { - border-bottom-left-radius: var(--bs-border-radius-pill) !important; - border-top-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; } .visible { - visibility: visible !important; + visibility: visible !important; } .invisible { - visibility: hidden !important; + visibility: hidden !important; } .z-n1 { - z-index: -1 !important; + z-index: -1 !important; } .z-0 { - z-index: 0 !important; + z-index: 0 !important; } .z-1 { - z-index: 1 !important; + z-index: 1 !important; } .z-2 { - z-index: 2 !important; + z-index: 2 !important; } .z-3 { - z-index: 3 !important; + z-index: 3 !important; } @media (min-width: 576px) { - .float-sm-start { - float: left !important; - } - .float-sm-end { - float: right !important; - } - .float-sm-none { - float: none !important; - } - .object-fit-sm-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-sm-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-sm-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-sm-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-sm-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-sm-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-sm-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-sm-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-sm-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-sm-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-sm-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-right: 0 !important; - } - .me-sm-1 { - margin-right: 0.25rem !important; - } - .me-sm-2 { - margin-right: 0.5rem !important; - } - .me-sm-3 { - margin-right: 1rem !important; - } - .me-sm-4 { - margin-right: 1.5rem !important; - } - .me-sm-5 { - margin-right: 3rem !important; - } - .me-sm-auto { - margin-right: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-left: 0 !important; - } - .ms-sm-1 { - margin-left: 0.25rem !important; - } - .ms-sm-2 { - margin-left: 0.5rem !important; - } - .ms-sm-3 { - margin-left: 1rem !important; - } - .ms-sm-4 { - margin-left: 1.5rem !important; - } - .ms-sm-5 { - margin-left: 3rem !important; - } - .ms-sm-auto { - margin-left: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-sm-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-sm-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-sm-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-sm-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-sm-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-right: 0 !important; - } - .pe-sm-1 { - padding-right: 0.25rem !important; - } - .pe-sm-2 { - padding-right: 0.5rem !important; - } - .pe-sm-3 { - padding-right: 1rem !important; - } - .pe-sm-4 { - padding-right: 1.5rem !important; - } - .pe-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-left: 0 !important; - } - .ps-sm-1 { - padding-left: 0.25rem !important; - } - .ps-sm-2 { - padding-left: 0.5rem !important; - } - .ps-sm-3 { - padding-left: 1rem !important; - } - .ps-sm-4 { - padding-left: 1.5rem !important; - } - .ps-sm-5 { - padding-left: 3rem !important; - } - .gap-sm-0 { - gap: 0 !important; - } - .gap-sm-1 { - gap: 0.25rem !important; - } - .gap-sm-2 { - gap: 0.5rem !important; - } - .gap-sm-3 { - gap: 1rem !important; - } - .gap-sm-4 { - gap: 1.5rem !important; - } - .gap-sm-5 { - gap: 3rem !important; - } - .row-gap-sm-0 { - row-gap: 0 !important; - } - .row-gap-sm-1 { - row-gap: 0.25rem !important; - } - .row-gap-sm-2 { - row-gap: 0.5rem !important; - } - .row-gap-sm-3 { - row-gap: 1rem !important; - } - .row-gap-sm-4 { - row-gap: 1.5rem !important; - } - .row-gap-sm-5 { - row-gap: 3rem !important; - } - .column-gap-sm-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-sm-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-sm-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-sm-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-sm-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-sm-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-sm-start { - text-align: left !important; - } - .text-sm-end { - text-align: right !important; - } - .text-sm-center { - text-align: center !important; - } -} -@media (min-width: 768px) { - .float-md-start { - float: left !important; - } - .float-md-end { - float: right !important; - } - .float-md-none { - float: none !important; - } - .object-fit-md-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-md-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-md-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-md-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-md-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-md-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-md-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-md-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-md-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-md-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-md-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-right: 0 !important; - } - .me-md-1 { - margin-right: 0.25rem !important; - } - .me-md-2 { - margin-right: 0.5rem !important; - } - .me-md-3 { - margin-right: 1rem !important; - } - .me-md-4 { - margin-right: 1.5rem !important; - } - .me-md-5 { - margin-right: 3rem !important; - } - .me-md-auto { - margin-right: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-left: 0 !important; - } - .ms-md-1 { - margin-left: 0.25rem !important; - } - .ms-md-2 { - margin-left: 0.5rem !important; - } - .ms-md-3 { - margin-left: 1rem !important; - } - .ms-md-4 { - margin-left: 1.5rem !important; - } - .ms-md-5 { - margin-left: 3rem !important; - } - .ms-md-auto { - margin-left: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-md-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-md-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-md-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-md-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-md-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-right: 0 !important; - } - .pe-md-1 { - padding-right: 0.25rem !important; - } - .pe-md-2 { - padding-right: 0.5rem !important; - } - .pe-md-3 { - padding-right: 1rem !important; - } - .pe-md-4 { - padding-right: 1.5rem !important; - } - .pe-md-5 { - padding-right: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-left: 0 !important; - } - .ps-md-1 { - padding-left: 0.25rem !important; - } - .ps-md-2 { - padding-left: 0.5rem !important; - } - .ps-md-3 { - padding-left: 1rem !important; - } - .ps-md-4 { - padding-left: 1.5rem !important; - } - .ps-md-5 { - padding-left: 3rem !important; - } - .gap-md-0 { - gap: 0 !important; - } - .gap-md-1 { - gap: 0.25rem !important; - } - .gap-md-2 { - gap: 0.5rem !important; - } - .gap-md-3 { - gap: 1rem !important; - } - .gap-md-4 { - gap: 1.5rem !important; - } - .gap-md-5 { - gap: 3rem !important; - } - .row-gap-md-0 { - row-gap: 0 !important; - } - .row-gap-md-1 { - row-gap: 0.25rem !important; - } - .row-gap-md-2 { - row-gap: 0.5rem !important; - } - .row-gap-md-3 { - row-gap: 1rem !important; - } - .row-gap-md-4 { - row-gap: 1.5rem !important; - } - .row-gap-md-5 { - row-gap: 3rem !important; - } - .column-gap-md-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-md-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-md-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-md-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-md-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-md-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-md-start { - text-align: left !important; - } - .text-md-end { - text-align: right !important; - } - .text-md-center { - text-align: center !important; - } -} -@media (min-width: 992px) { - .float-lg-start { - float: left !important; - } - .float-lg-end { - float: right !important; - } - .float-lg-none { - float: none !important; - } - .object-fit-lg-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-lg-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-lg-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-lg-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-lg-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-lg-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-lg-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-lg-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-lg-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-lg-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-lg-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-right: 0 !important; - } - .me-lg-1 { - margin-right: 0.25rem !important; - } - .me-lg-2 { - margin-right: 0.5rem !important; - } - .me-lg-3 { - margin-right: 1rem !important; - } - .me-lg-4 { - margin-right: 1.5rem !important; - } - .me-lg-5 { - margin-right: 3rem !important; - } - .me-lg-auto { - margin-right: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-left: 0 !important; - } - .ms-lg-1 { - margin-left: 0.25rem !important; - } - .ms-lg-2 { - margin-left: 0.5rem !important; - } - .ms-lg-3 { - margin-left: 1rem !important; - } - .ms-lg-4 { - margin-left: 1.5rem !important; - } - .ms-lg-5 { - margin-left: 3rem !important; - } - .ms-lg-auto { - margin-left: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-lg-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-lg-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-lg-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-lg-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-lg-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-right: 0 !important; - } - .pe-lg-1 { - padding-right: 0.25rem !important; - } - .pe-lg-2 { - padding-right: 0.5rem !important; - } - .pe-lg-3 { - padding-right: 1rem !important; - } - .pe-lg-4 { - padding-right: 1.5rem !important; - } - .pe-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-left: 0 !important; - } - .ps-lg-1 { - padding-left: 0.25rem !important; - } - .ps-lg-2 { - padding-left: 0.5rem !important; - } - .ps-lg-3 { - padding-left: 1rem !important; - } - .ps-lg-4 { - padding-left: 1.5rem !important; - } - .ps-lg-5 { - padding-left: 3rem !important; - } - .gap-lg-0 { - gap: 0 !important; - } - .gap-lg-1 { - gap: 0.25rem !important; - } - .gap-lg-2 { - gap: 0.5rem !important; - } - .gap-lg-3 { - gap: 1rem !important; - } - .gap-lg-4 { - gap: 1.5rem !important; - } - .gap-lg-5 { - gap: 3rem !important; - } - .row-gap-lg-0 { - row-gap: 0 !important; - } - .row-gap-lg-1 { - row-gap: 0.25rem !important; - } - .row-gap-lg-2 { - row-gap: 0.5rem !important; - } - .row-gap-lg-3 { - row-gap: 1rem !important; - } - .row-gap-lg-4 { - row-gap: 1.5rem !important; - } - .row-gap-lg-5 { - row-gap: 3rem !important; - } - .column-gap-lg-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-lg-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-lg-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-lg-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-lg-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-lg-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-lg-start { - text-align: left !important; - } - .text-lg-end { - text-align: right !important; - } - .text-lg-center { - text-align: center !important; - } -} -@media (min-width: 1200px) { - .float-xl-start { - float: left !important; - } - .float-xl-end { - float: right !important; - } - .float-xl-none { - float: none !important; - } - .object-fit-xl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-right: 0 !important; - } - .me-xl-1 { - margin-right: 0.25rem !important; - } - .me-xl-2 { - margin-right: 0.5rem !important; - } - .me-xl-3 { - margin-right: 1rem !important; - } - .me-xl-4 { - margin-right: 1.5rem !important; - } - .me-xl-5 { - margin-right: 3rem !important; - } - .me-xl-auto { - margin-right: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-left: 0 !important; - } - .ms-xl-1 { - margin-left: 0.25rem !important; - } - .ms-xl-2 { - margin-left: 0.5rem !important; - } - .ms-xl-3 { - margin-left: 1rem !important; - } - .ms-xl-4 { - margin-left: 1.5rem !important; - } - .ms-xl-5 { - margin-left: 3rem !important; - } - .ms-xl-auto { - margin-left: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-right: 0 !important; - } - .pe-xl-1 { - padding-right: 0.25rem !important; - } - .pe-xl-2 { - padding-right: 0.5rem !important; - } - .pe-xl-3 { - padding-right: 1rem !important; - } - .pe-xl-4 { - padding-right: 1.5rem !important; - } - .pe-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-left: 0 !important; - } - .ps-xl-1 { - padding-left: 0.25rem !important; - } - .ps-xl-2 { - padding-left: 0.5rem !important; - } - .ps-xl-3 { - padding-left: 1rem !important; - } - .ps-xl-4 { - padding-left: 1.5rem !important; - } - .ps-xl-5 { - padding-left: 3rem !important; - } - .gap-xl-0 { - gap: 0 !important; - } - .gap-xl-1 { - gap: 0.25rem !important; - } - .gap-xl-2 { - gap: 0.5rem !important; - } - .gap-xl-3 { - gap: 1rem !important; - } - .gap-xl-4 { - gap: 1.5rem !important; - } - .gap-xl-5 { - gap: 3rem !important; - } - .row-gap-xl-0 { - row-gap: 0 !important; - } - .row-gap-xl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xl-3 { - row-gap: 1rem !important; - } - .row-gap-xl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xl-5 { - row-gap: 3rem !important; - } - .column-gap-xl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xl-start { - text-align: left !important; - } - .text-xl-end { - text-align: right !important; - } - .text-xl-center { - text-align: center !important; - } -} -@media (min-width: 1400px) { - .float-xxl-start { - float: left !important; - } - .float-xxl-end { - float: right !important; - } - .float-xxl-none { - float: none !important; - } - .object-fit-xxl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xxl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xxl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xxl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xxl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xxl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xxl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xxl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xxl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xxl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xxl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-right: 0 !important; - } - .me-xxl-1 { - margin-right: 0.25rem !important; - } - .me-xxl-2 { - margin-right: 0.5rem !important; - } - .me-xxl-3 { - margin-right: 1rem !important; - } - .me-xxl-4 { - margin-right: 1.5rem !important; - } - .me-xxl-5 { - margin-right: 3rem !important; - } - .me-xxl-auto { - margin-right: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-left: 0 !important; - } - .ms-xxl-1 { - margin-left: 0.25rem !important; - } - .ms-xxl-2 { - margin-left: 0.5rem !important; - } - .ms-xxl-3 { - margin-left: 1rem !important; - } - .ms-xxl-4 { - margin-left: 1.5rem !important; - } - .ms-xxl-5 { - margin-left: 3rem !important; - } - .ms-xxl-auto { - margin-left: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xxl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xxl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xxl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xxl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xxl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-right: 0 !important; - } - .pe-xxl-1 { - padding-right: 0.25rem !important; - } - .pe-xxl-2 { - padding-right: 0.5rem !important; - } - .pe-xxl-3 { - padding-right: 1rem !important; - } - .pe-xxl-4 { - padding-right: 1.5rem !important; - } - .pe-xxl-5 { - padding-right: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-left: 0 !important; - } - .ps-xxl-1 { - padding-left: 0.25rem !important; - } - .ps-xxl-2 { - padding-left: 0.5rem !important; - } - .ps-xxl-3 { - padding-left: 1rem !important; - } - .ps-xxl-4 { - padding-left: 1.5rem !important; - } - .ps-xxl-5 { - padding-left: 3rem !important; - } - .gap-xxl-0 { - gap: 0 !important; - } - .gap-xxl-1 { - gap: 0.25rem !important; - } - .gap-xxl-2 { - gap: 0.5rem !important; - } - .gap-xxl-3 { - gap: 1rem !important; - } - .gap-xxl-4 { - gap: 1.5rem !important; - } - .gap-xxl-5 { - gap: 3rem !important; - } - .row-gap-xxl-0 { - row-gap: 0 !important; - } - .row-gap-xxl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xxl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xxl-3 { - row-gap: 1rem !important; - } - .row-gap-xxl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xxl-5 { - row-gap: 3rem !important; - } - .column-gap-xxl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xxl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xxl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xxl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xxl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xxl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xxl-start { - text-align: left !important; - } - .text-xxl-end { - text-align: right !important; - } - .text-xxl-center { - text-align: center !important; - } + .float-sm-start { + float: left !important; + } + + .float-sm-end { + float: right !important; + } + + .float-sm-none { + float: none !important; + } + + .object-fit-sm-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-sm-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-sm-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-sm-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-sm-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-grid { + display: grid !important; + } + + .d-sm-inline-grid { + display: inline-grid !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: flex !important; + } + + .d-sm-inline-flex { + display: inline-flex !important; + } + + .d-sm-none { + display: none !important; + } + + .flex-sm-fill { + flex: 1 1 auto !important; + } + + .flex-sm-row { + flex-direction: row !important; + } + + .flex-sm-column { + flex-direction: column !important; + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-sm-wrap { + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-sm-start { + justify-content: flex-start !important; + } + + .justify-content-sm-end { + justify-content: flex-end !important; + } + + .justify-content-sm-center { + justify-content: center !important; + } + + .justify-content-sm-between { + justify-content: space-between !important; + } + + .justify-content-sm-around { + justify-content: space-around !important; + } + + .justify-content-sm-evenly { + justify-content: space-evenly !important; + } + + .align-items-sm-start { + align-items: flex-start !important; + } + + .align-items-sm-end { + align-items: flex-end !important; + } + + .align-items-sm-center { + align-items: center !important; + } + + .align-items-sm-baseline { + align-items: baseline !important; + } + + .align-items-sm-stretch { + align-items: stretch !important; + } + + .align-content-sm-start { + align-content: flex-start !important; + } + + .align-content-sm-end { + align-content: flex-end !important; + } + + .align-content-sm-center { + align-content: center !important; + } + + .align-content-sm-between { + align-content: space-between !important; + } + + .align-content-sm-around { + align-content: space-around !important; + } + + .align-content-sm-stretch { + align-content: stretch !important; + } + + .align-self-sm-auto { + align-self: auto !important; + } + + .align-self-sm-start { + align-self: flex-start !important; + } + + .align-self-sm-end { + align-self: flex-end !important; + } + + .align-self-sm-center { + align-self: center !important; + } + + .align-self-sm-baseline { + align-self: baseline !important; + } + + .align-self-sm-stretch { + align-self: stretch !important; + } + + .order-sm-first { + order: -1 !important; + } + + .order-sm-0 { + order: 0 !important; + } + + .order-sm-1 { + order: 1 !important; + } + + .order-sm-2 { + order: 2 !important; + } + + .order-sm-3 { + order: 3 !important; + } + + .order-sm-4 { + order: 4 !important; + } + + .order-sm-5 { + order: 5 !important; + } + + .order-sm-last { + order: 6 !important; + } + + .m-sm-0 { + margin: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mx-sm-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-sm-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-sm-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-sm-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-sm-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-sm-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-sm-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-sm-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-sm-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-sm-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-sm-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-sm-0 { + margin-top: 0 !important; + } + + .mt-sm-1 { + margin-top: 0.25rem !important; + } + + .mt-sm-2 { + margin-top: 0.5rem !important; + } + + .mt-sm-3 { + margin-top: 1rem !important; + } + + .mt-sm-4 { + margin-top: 1.5rem !important; + } + + .mt-sm-5 { + margin-top: 3rem !important; + } + + .mt-sm-auto { + margin-top: auto !important; + } + + .me-sm-0 { + margin-right: 0 !important; + } + + .me-sm-1 { + margin-right: 0.25rem !important; + } + + .me-sm-2 { + margin-right: 0.5rem !important; + } + + .me-sm-3 { + margin-right: 1rem !important; + } + + .me-sm-4 { + margin-right: 1.5rem !important; + } + + .me-sm-5 { + margin-right: 3rem !important; + } + + .me-sm-auto { + margin-right: auto !important; + } + + .mb-sm-0 { + margin-bottom: 0 !important; + } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; + } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; + } + + .mb-sm-3 { + margin-bottom: 1rem !important; + } + + .mb-sm-4 { + margin-bottom: 1.5rem !important; + } + + .mb-sm-5 { + margin-bottom: 3rem !important; + } + + .mb-sm-auto { + margin-bottom: auto !important; + } + + .ms-sm-0 { + margin-left: 0 !important; + } + + .ms-sm-1 { + margin-left: 0.25rem !important; + } + + .ms-sm-2 { + margin-left: 0.5rem !important; + } + + .ms-sm-3 { + margin-left: 1rem !important; + } + + .ms-sm-4 { + margin-left: 1.5rem !important; + } + + .ms-sm-5 { + margin-left: 3rem !important; + } + + .ms-sm-auto { + margin-left: auto !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .px-sm-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-sm-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-sm-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-sm-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-sm-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-sm-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-sm-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-sm-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-sm-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-sm-0 { + padding-top: 0 !important; + } + + .pt-sm-1 { + padding-top: 0.25rem !important; + } + + .pt-sm-2 { + padding-top: 0.5rem !important; + } + + .pt-sm-3 { + padding-top: 1rem !important; + } + + .pt-sm-4 { + padding-top: 1.5rem !important; + } + + .pt-sm-5 { + padding-top: 3rem !important; + } + + .pe-sm-0 { + padding-right: 0 !important; + } + + .pe-sm-1 { + padding-right: 0.25rem !important; + } + + .pe-sm-2 { + padding-right: 0.5rem !important; + } + + .pe-sm-3 { + padding-right: 1rem !important; + } + + .pe-sm-4 { + padding-right: 1.5rem !important; + } + + .pe-sm-5 { + padding-right: 3rem !important; + } + + .pb-sm-0 { + padding-bottom: 0 !important; + } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pb-sm-3 { + padding-bottom: 1rem !important; + } + + .pb-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pb-sm-5 { + padding-bottom: 3rem !important; + } + + .ps-sm-0 { + padding-left: 0 !important; + } + + .ps-sm-1 { + padding-left: 0.25rem !important; + } + + .ps-sm-2 { + padding-left: 0.5rem !important; + } + + .ps-sm-3 { + padding-left: 1rem !important; + } + + .ps-sm-4 { + padding-left: 1.5rem !important; + } + + .ps-sm-5 { + padding-left: 3rem !important; + } + + .gap-sm-0 { + gap: 0 !important; + } + + .gap-sm-1 { + gap: 0.25rem !important; + } + + .gap-sm-2 { + gap: 0.5rem !important; + } + + .gap-sm-3 { + gap: 1rem !important; + } + + .gap-sm-4 { + gap: 1.5rem !important; + } + + .gap-sm-5 { + gap: 3rem !important; + } + + .row-gap-sm-0 { + row-gap: 0 !important; + } + + .row-gap-sm-1 { + row-gap: 0.25rem !important; + } + + .row-gap-sm-2 { + row-gap: 0.5rem !important; + } + + .row-gap-sm-3 { + row-gap: 1rem !important; + } + + .row-gap-sm-4 { + row-gap: 1.5rem !important; + } + + .row-gap-sm-5 { + row-gap: 3rem !important; + } + + .column-gap-sm-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-sm-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-sm-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-sm-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-sm-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-sm-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-sm-start { + text-align: left !important; + } + + .text-sm-end { + text-align: right !important; + } + + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .float-md-start { + float: left !important; + } + + .float-md-end { + float: right !important; + } + + .float-md-none { + float: none !important; + } + + .object-fit-md-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-md-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-md-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-md-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-md-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-grid { + display: grid !important; + } + + .d-md-inline-grid { + display: inline-grid !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: flex !important; + } + + .d-md-inline-flex { + display: inline-flex !important; + } + + .d-md-none { + display: none !important; + } + + .flex-md-fill { + flex: 1 1 auto !important; + } + + .flex-md-row { + flex-direction: row !important; + } + + .flex-md-column { + flex-direction: column !important; + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-md-grow-0 { + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-md-wrap { + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-md-start { + justify-content: flex-start !important; + } + + .justify-content-md-end { + justify-content: flex-end !important; + } + + .justify-content-md-center { + justify-content: center !important; + } + + .justify-content-md-between { + justify-content: space-between !important; + } + + .justify-content-md-around { + justify-content: space-around !important; + } + + .justify-content-md-evenly { + justify-content: space-evenly !important; + } + + .align-items-md-start { + align-items: flex-start !important; + } + + .align-items-md-end { + align-items: flex-end !important; + } + + .align-items-md-center { + align-items: center !important; + } + + .align-items-md-baseline { + align-items: baseline !important; + } + + .align-items-md-stretch { + align-items: stretch !important; + } + + .align-content-md-start { + align-content: flex-start !important; + } + + .align-content-md-end { + align-content: flex-end !important; + } + + .align-content-md-center { + align-content: center !important; + } + + .align-content-md-between { + align-content: space-between !important; + } + + .align-content-md-around { + align-content: space-around !important; + } + + .align-content-md-stretch { + align-content: stretch !important; + } + + .align-self-md-auto { + align-self: auto !important; + } + + .align-self-md-start { + align-self: flex-start !important; + } + + .align-self-md-end { + align-self: flex-end !important; + } + + .align-self-md-center { + align-self: center !important; + } + + .align-self-md-baseline { + align-self: baseline !important; + } + + .align-self-md-stretch { + align-self: stretch !important; + } + + .order-md-first { + order: -1 !important; + } + + .order-md-0 { + order: 0 !important; + } + + .order-md-1 { + order: 1 !important; + } + + .order-md-2 { + order: 2 !important; + } + + .order-md-3 { + order: 3 !important; + } + + .order-md-4 { + order: 4 !important; + } + + .order-md-5 { + order: 5 !important; + } + + .order-md-last { + order: 6 !important; + } + + .m-md-0 { + margin: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mx-md-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-md-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-md-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-md-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-md-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-md-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-md-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-md-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-md-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-md-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-md-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-md-0 { + margin-top: 0 !important; + } + + .mt-md-1 { + margin-top: 0.25rem !important; + } + + .mt-md-2 { + margin-top: 0.5rem !important; + } + + .mt-md-3 { + margin-top: 1rem !important; + } + + .mt-md-4 { + margin-top: 1.5rem !important; + } + + .mt-md-5 { + margin-top: 3rem !important; + } + + .mt-md-auto { + margin-top: auto !important; + } + + .me-md-0 { + margin-right: 0 !important; + } + + .me-md-1 { + margin-right: 0.25rem !important; + } + + .me-md-2 { + margin-right: 0.5rem !important; + } + + .me-md-3 { + margin-right: 1rem !important; + } + + .me-md-4 { + margin-right: 1.5rem !important; + } + + .me-md-5 { + margin-right: 3rem !important; + } + + .me-md-auto { + margin-right: auto !important; + } + + .mb-md-0 { + margin-bottom: 0 !important; + } + + .mb-md-1 { + margin-bottom: 0.25rem !important; + } + + .mb-md-2 { + margin-bottom: 0.5rem !important; + } + + .mb-md-3 { + margin-bottom: 1rem !important; + } + + .mb-md-4 { + margin-bottom: 1.5rem !important; + } + + .mb-md-5 { + margin-bottom: 3rem !important; + } + + .mb-md-auto { + margin-bottom: auto !important; + } + + .ms-md-0 { + margin-left: 0 !important; + } + + .ms-md-1 { + margin-left: 0.25rem !important; + } + + .ms-md-2 { + margin-left: 0.5rem !important; + } + + .ms-md-3 { + margin-left: 1rem !important; + } + + .ms-md-4 { + margin-left: 1.5rem !important; + } + + .ms-md-5 { + margin-left: 3rem !important; + } + + .ms-md-auto { + margin-left: auto !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .px-md-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-md-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-md-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-md-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-md-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-md-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-md-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-md-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-md-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-md-0 { + padding-top: 0 !important; + } + + .pt-md-1 { + padding-top: 0.25rem !important; + } + + .pt-md-2 { + padding-top: 0.5rem !important; + } + + .pt-md-3 { + padding-top: 1rem !important; + } + + .pt-md-4 { + padding-top: 1.5rem !important; + } + + .pt-md-5 { + padding-top: 3rem !important; + } + + .pe-md-0 { + padding-right: 0 !important; + } + + .pe-md-1 { + padding-right: 0.25rem !important; + } + + .pe-md-2 { + padding-right: 0.5rem !important; + } + + .pe-md-3 { + padding-right: 1rem !important; + } + + .pe-md-4 { + padding-right: 1.5rem !important; + } + + .pe-md-5 { + padding-right: 3rem !important; + } + + .pb-md-0 { + padding-bottom: 0 !important; + } + + .pb-md-1 { + padding-bottom: 0.25rem !important; + } + + .pb-md-2 { + padding-bottom: 0.5rem !important; + } + + .pb-md-3 { + padding-bottom: 1rem !important; + } + + .pb-md-4 { + padding-bottom: 1.5rem !important; + } + + .pb-md-5 { + padding-bottom: 3rem !important; + } + + .ps-md-0 { + padding-left: 0 !important; + } + + .ps-md-1 { + padding-left: 0.25rem !important; + } + + .ps-md-2 { + padding-left: 0.5rem !important; + } + + .ps-md-3 { + padding-left: 1rem !important; + } + + .ps-md-4 { + padding-left: 1.5rem !important; + } + + .ps-md-5 { + padding-left: 3rem !important; + } + + .gap-md-0 { + gap: 0 !important; + } + + .gap-md-1 { + gap: 0.25rem !important; + } + + .gap-md-2 { + gap: 0.5rem !important; + } + + .gap-md-3 { + gap: 1rem !important; + } + + .gap-md-4 { + gap: 1.5rem !important; + } + + .gap-md-5 { + gap: 3rem !important; + } + + .row-gap-md-0 { + row-gap: 0 !important; + } + + .row-gap-md-1 { + row-gap: 0.25rem !important; + } + + .row-gap-md-2 { + row-gap: 0.5rem !important; + } + + .row-gap-md-3 { + row-gap: 1rem !important; + } + + .row-gap-md-4 { + row-gap: 1.5rem !important; + } + + .row-gap-md-5 { + row-gap: 3rem !important; + } + + .column-gap-md-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-md-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-md-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-md-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-md-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-md-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-md-start { + text-align: left !important; + } + + .text-md-end { + text-align: right !important; + } + + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .float-lg-start { + float: left !important; + } + + .float-lg-end { + float: right !important; + } + + .float-lg-none { + float: none !important; + } + + .object-fit-lg-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-lg-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-lg-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-lg-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-lg-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-grid { + display: grid !important; + } + + .d-lg-inline-grid { + display: inline-grid !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: flex !important; + } + + .d-lg-inline-flex { + display: inline-flex !important; + } + + .d-lg-none { + display: none !important; + } + + .flex-lg-fill { + flex: 1 1 auto !important; + } + + .flex-lg-row { + flex-direction: row !important; + } + + .flex-lg-column { + flex-direction: column !important; + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-lg-wrap { + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-lg-start { + justify-content: flex-start !important; + } + + .justify-content-lg-end { + justify-content: flex-end !important; + } + + .justify-content-lg-center { + justify-content: center !important; + } + + .justify-content-lg-between { + justify-content: space-between !important; + } + + .justify-content-lg-around { + justify-content: space-around !important; + } + + .justify-content-lg-evenly { + justify-content: space-evenly !important; + } + + .align-items-lg-start { + align-items: flex-start !important; + } + + .align-items-lg-end { + align-items: flex-end !important; + } + + .align-items-lg-center { + align-items: center !important; + } + + .align-items-lg-baseline { + align-items: baseline !important; + } + + .align-items-lg-stretch { + align-items: stretch !important; + } + + .align-content-lg-start { + align-content: flex-start !important; + } + + .align-content-lg-end { + align-content: flex-end !important; + } + + .align-content-lg-center { + align-content: center !important; + } + + .align-content-lg-between { + align-content: space-between !important; + } + + .align-content-lg-around { + align-content: space-around !important; + } + + .align-content-lg-stretch { + align-content: stretch !important; + } + + .align-self-lg-auto { + align-self: auto !important; + } + + .align-self-lg-start { + align-self: flex-start !important; + } + + .align-self-lg-end { + align-self: flex-end !important; + } + + .align-self-lg-center { + align-self: center !important; + } + + .align-self-lg-baseline { + align-self: baseline !important; + } + + .align-self-lg-stretch { + align-self: stretch !important; + } + + .order-lg-first { + order: -1 !important; + } + + .order-lg-0 { + order: 0 !important; + } + + .order-lg-1 { + order: 1 !important; + } + + .order-lg-2 { + order: 2 !important; + } + + .order-lg-3 { + order: 3 !important; + } + + .order-lg-4 { + order: 4 !important; + } + + .order-lg-5 { + order: 5 !important; + } + + .order-lg-last { + order: 6 !important; + } + + .m-lg-0 { + margin: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mx-lg-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-lg-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-lg-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-lg-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-lg-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-lg-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-lg-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-lg-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-lg-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-lg-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-lg-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-lg-0 { + margin-top: 0 !important; + } + + .mt-lg-1 { + margin-top: 0.25rem !important; + } + + .mt-lg-2 { + margin-top: 0.5rem !important; + } + + .mt-lg-3 { + margin-top: 1rem !important; + } + + .mt-lg-4 { + margin-top: 1.5rem !important; + } + + .mt-lg-5 { + margin-top: 3rem !important; + } + + .mt-lg-auto { + margin-top: auto !important; + } + + .me-lg-0 { + margin-right: 0 !important; + } + + .me-lg-1 { + margin-right: 0.25rem !important; + } + + .me-lg-2 { + margin-right: 0.5rem !important; + } + + .me-lg-3 { + margin-right: 1rem !important; + } + + .me-lg-4 { + margin-right: 1.5rem !important; + } + + .me-lg-5 { + margin-right: 3rem !important; + } + + .me-lg-auto { + margin-right: auto !important; + } + + .mb-lg-0 { + margin-bottom: 0 !important; + } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; + } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; + } + + .mb-lg-3 { + margin-bottom: 1rem !important; + } + + .mb-lg-4 { + margin-bottom: 1.5rem !important; + } + + .mb-lg-5 { + margin-bottom: 3rem !important; + } + + .mb-lg-auto { + margin-bottom: auto !important; + } + + .ms-lg-0 { + margin-left: 0 !important; + } + + .ms-lg-1 { + margin-left: 0.25rem !important; + } + + .ms-lg-2 { + margin-left: 0.5rem !important; + } + + .ms-lg-3 { + margin-left: 1rem !important; + } + + .ms-lg-4 { + margin-left: 1.5rem !important; + } + + .ms-lg-5 { + margin-left: 3rem !important; + } + + .ms-lg-auto { + margin-left: auto !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .px-lg-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-lg-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-lg-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-lg-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-lg-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-lg-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-lg-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-lg-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-lg-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-lg-0 { + padding-top: 0 !important; + } + + .pt-lg-1 { + padding-top: 0.25rem !important; + } + + .pt-lg-2 { + padding-top: 0.5rem !important; + } + + .pt-lg-3 { + padding-top: 1rem !important; + } + + .pt-lg-4 { + padding-top: 1.5rem !important; + } + + .pt-lg-5 { + padding-top: 3rem !important; + } + + .pe-lg-0 { + padding-right: 0 !important; + } + + .pe-lg-1 { + padding-right: 0.25rem !important; + } + + .pe-lg-2 { + padding-right: 0.5rem !important; + } + + .pe-lg-3 { + padding-right: 1rem !important; + } + + .pe-lg-4 { + padding-right: 1.5rem !important; + } + + .pe-lg-5 { + padding-right: 3rem !important; + } + + .pb-lg-0 { + padding-bottom: 0 !important; + } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pb-lg-3 { + padding-bottom: 1rem !important; + } + + .pb-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pb-lg-5 { + padding-bottom: 3rem !important; + } + + .ps-lg-0 { + padding-left: 0 !important; + } + + .ps-lg-1 { + padding-left: 0.25rem !important; + } + + .ps-lg-2 { + padding-left: 0.5rem !important; + } + + .ps-lg-3 { + padding-left: 1rem !important; + } + + .ps-lg-4 { + padding-left: 1.5rem !important; + } + + .ps-lg-5 { + padding-left: 3rem !important; + } + + .gap-lg-0 { + gap: 0 !important; + } + + .gap-lg-1 { + gap: 0.25rem !important; + } + + .gap-lg-2 { + gap: 0.5rem !important; + } + + .gap-lg-3 { + gap: 1rem !important; + } + + .gap-lg-4 { + gap: 1.5rem !important; + } + + .gap-lg-5 { + gap: 3rem !important; + } + + .row-gap-lg-0 { + row-gap: 0 !important; + } + + .row-gap-lg-1 { + row-gap: 0.25rem !important; + } + + .row-gap-lg-2 { + row-gap: 0.5rem !important; + } + + .row-gap-lg-3 { + row-gap: 1rem !important; + } + + .row-gap-lg-4 { + row-gap: 1.5rem !important; + } + + .row-gap-lg-5 { + row-gap: 3rem !important; + } + + .column-gap-lg-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-lg-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-lg-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-lg-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-lg-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-lg-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-lg-start { + text-align: left !important; + } + + .text-lg-end { + text-align: right !important; + } + + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .float-xl-start { + float: left !important; + } + + .float-xl-end { + float: right !important; + } + + .float-xl-none { + float: none !important; + } + + .object-fit-xl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-grid { + display: grid !important; + } + + .d-xl-inline-grid { + display: inline-grid !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: flex !important; + } + + .d-xl-inline-flex { + display: inline-flex !important; + } + + .d-xl-none { + display: none !important; + } + + .flex-xl-fill { + flex: 1 1 auto !important; + } + + .flex-xl-row { + flex-direction: row !important; + } + + .flex-xl-column { + flex-direction: column !important; + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xl-wrap { + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xl-start { + justify-content: flex-start !important; + } + + .justify-content-xl-end { + justify-content: flex-end !important; + } + + .justify-content-xl-center { + justify-content: center !important; + } + + .justify-content-xl-between { + justify-content: space-between !important; + } + + .justify-content-xl-around { + justify-content: space-around !important; + } + + .justify-content-xl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xl-start { + align-items: flex-start !important; + } + + .align-items-xl-end { + align-items: flex-end !important; + } + + .align-items-xl-center { + align-items: center !important; + } + + .align-items-xl-baseline { + align-items: baseline !important; + } + + .align-items-xl-stretch { + align-items: stretch !important; + } + + .align-content-xl-start { + align-content: flex-start !important; + } + + .align-content-xl-end { + align-content: flex-end !important; + } + + .align-content-xl-center { + align-content: center !important; + } + + .align-content-xl-between { + align-content: space-between !important; + } + + .align-content-xl-around { + align-content: space-around !important; + } + + .align-content-xl-stretch { + align-content: stretch !important; + } + + .align-self-xl-auto { + align-self: auto !important; + } + + .align-self-xl-start { + align-self: flex-start !important; + } + + .align-self-xl-end { + align-self: flex-end !important; + } + + .align-self-xl-center { + align-self: center !important; + } + + .align-self-xl-baseline { + align-self: baseline !important; + } + + .align-self-xl-stretch { + align-self: stretch !important; + } + + .order-xl-first { + order: -1 !important; + } + + .order-xl-0 { + order: 0 !important; + } + + .order-xl-1 { + order: 1 !important; + } + + .order-xl-2 { + order: 2 !important; + } + + .order-xl-3 { + order: 3 !important; + } + + .order-xl-4 { + order: 4 !important; + } + + .order-xl-5 { + order: 5 !important; + } + + .order-xl-last { + order: 6 !important; + } + + .m-xl-0 { + margin: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mx-xl-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-xl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-xl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-xl-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-xl-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-xl-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-xl-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xl-0 { + margin-top: 0 !important; + } + + .mt-xl-1 { + margin-top: 0.25rem !important; + } + + .mt-xl-2 { + margin-top: 0.5rem !important; + } + + .mt-xl-3 { + margin-top: 1rem !important; + } + + .mt-xl-4 { + margin-top: 1.5rem !important; + } + + .mt-xl-5 { + margin-top: 3rem !important; + } + + .mt-xl-auto { + margin-top: auto !important; + } + + .me-xl-0 { + margin-right: 0 !important; + } + + .me-xl-1 { + margin-right: 0.25rem !important; + } + + .me-xl-2 { + margin-right: 0.5rem !important; + } + + .me-xl-3 { + margin-right: 1rem !important; + } + + .me-xl-4 { + margin-right: 1.5rem !important; + } + + .me-xl-5 { + margin-right: 3rem !important; + } + + .me-xl-auto { + margin-right: auto !important; + } + + .mb-xl-0 { + margin-bottom: 0 !important; + } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xl-3 { + margin-bottom: 1rem !important; + } + + .mb-xl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xl-5 { + margin-bottom: 3rem !important; + } + + .mb-xl-auto { + margin-bottom: auto !important; + } + + .ms-xl-0 { + margin-left: 0 !important; + } + + .ms-xl-1 { + margin-left: 0.25rem !important; + } + + .ms-xl-2 { + margin-left: 0.5rem !important; + } + + .ms-xl-3 { + margin-left: 1rem !important; + } + + .ms-xl-4 { + margin-left: 1.5rem !important; + } + + .ms-xl-5 { + margin-left: 3rem !important; + } + + .ms-xl-auto { + margin-left: auto !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .px-xl-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-xl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-xl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-xl-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-xl-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-xl-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xl-0 { + padding-top: 0 !important; + } + + .pt-xl-1 { + padding-top: 0.25rem !important; + } + + .pt-xl-2 { + padding-top: 0.5rem !important; + } + + .pt-xl-3 { + padding-top: 1rem !important; + } + + .pt-xl-4 { + padding-top: 1.5rem !important; + } + + .pt-xl-5 { + padding-top: 3rem !important; + } + + .pe-xl-0 { + padding-right: 0 !important; + } + + .pe-xl-1 { + padding-right: 0.25rem !important; + } + + .pe-xl-2 { + padding-right: 0.5rem !important; + } + + .pe-xl-3 { + padding-right: 1rem !important; + } + + .pe-xl-4 { + padding-right: 1.5rem !important; + } + + .pe-xl-5 { + padding-right: 3rem !important; + } + + .pb-xl-0 { + padding-bottom: 0 !important; + } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xl-3 { + padding-bottom: 1rem !important; + } + + .pb-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xl-5 { + padding-bottom: 3rem !important; + } + + .ps-xl-0 { + padding-left: 0 !important; + } + + .ps-xl-1 { + padding-left: 0.25rem !important; + } + + .ps-xl-2 { + padding-left: 0.5rem !important; + } + + .ps-xl-3 { + padding-left: 1rem !important; + } + + .ps-xl-4 { + padding-left: 1.5rem !important; + } + + .ps-xl-5 { + padding-left: 3rem !important; + } + + .gap-xl-0 { + gap: 0 !important; + } + + .gap-xl-1 { + gap: 0.25rem !important; + } + + .gap-xl-2 { + gap: 0.5rem !important; + } + + .gap-xl-3 { + gap: 1rem !important; + } + + .gap-xl-4 { + gap: 1.5rem !important; + } + + .gap-xl-5 { + gap: 3rem !important; + } + + .row-gap-xl-0 { + row-gap: 0 !important; + } + + .row-gap-xl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xl-3 { + row-gap: 1rem !important; + } + + .row-gap-xl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xl-5 { + row-gap: 3rem !important; + } + + .column-gap-xl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xl-start { + text-align: left !important; + } + + .text-xl-end { + text-align: right !important; + } + + .text-xl-center { + text-align: center !important; + } +} + +@media (min-width: 1400px) { + .float-xxl-start { + float: left !important; + } + + .float-xxl-end { + float: right !important; + } + + .float-xxl-none { + float: none !important; + } + + .object-fit-xxl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xxl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xxl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xxl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xxl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xxl-inline { + display: inline !important; + } + + .d-xxl-inline-block { + display: inline-block !important; + } + + .d-xxl-block { + display: block !important; + } + + .d-xxl-grid { + display: grid !important; + } + + .d-xxl-inline-grid { + display: inline-grid !important; + } + + .d-xxl-table { + display: table !important; + } + + .d-xxl-table-row { + display: table-row !important; + } + + .d-xxl-table-cell { + display: table-cell !important; + } + + .d-xxl-flex { + display: flex !important; + } + + .d-xxl-inline-flex { + display: inline-flex !important; + } + + .d-xxl-none { + display: none !important; + } + + .flex-xxl-fill { + flex: 1 1 auto !important; + } + + .flex-xxl-row { + flex-direction: row !important; + } + + .flex-xxl-column { + flex-direction: column !important; + } + + .flex-xxl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xxl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xxl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xxl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xxl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xxl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xxl-wrap { + flex-wrap: wrap !important; + } + + .flex-xxl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xxl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xxl-start { + justify-content: flex-start !important; + } + + .justify-content-xxl-end { + justify-content: flex-end !important; + } + + .justify-content-xxl-center { + justify-content: center !important; + } + + .justify-content-xxl-between { + justify-content: space-between !important; + } + + .justify-content-xxl-around { + justify-content: space-around !important; + } + + .justify-content-xxl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xxl-start { + align-items: flex-start !important; + } + + .align-items-xxl-end { + align-items: flex-end !important; + } + + .align-items-xxl-center { + align-items: center !important; + } + + .align-items-xxl-baseline { + align-items: baseline !important; + } + + .align-items-xxl-stretch { + align-items: stretch !important; + } + + .align-content-xxl-start { + align-content: flex-start !important; + } + + .align-content-xxl-end { + align-content: flex-end !important; + } + + .align-content-xxl-center { + align-content: center !important; + } + + .align-content-xxl-between { + align-content: space-between !important; + } + + .align-content-xxl-around { + align-content: space-around !important; + } + + .align-content-xxl-stretch { + align-content: stretch !important; + } + + .align-self-xxl-auto { + align-self: auto !important; + } + + .align-self-xxl-start { + align-self: flex-start !important; + } + + .align-self-xxl-end { + align-self: flex-end !important; + } + + .align-self-xxl-center { + align-self: center !important; + } + + .align-self-xxl-baseline { + align-self: baseline !important; + } + + .align-self-xxl-stretch { + align-self: stretch !important; + } + + .order-xxl-first { + order: -1 !important; + } + + .order-xxl-0 { + order: 0 !important; + } + + .order-xxl-1 { + order: 1 !important; + } + + .order-xxl-2 { + order: 2 !important; + } + + .order-xxl-3 { + order: 3 !important; + } + + .order-xxl-4 { + order: 4 !important; + } + + .order-xxl-5 { + order: 5 !important; + } + + .order-xxl-last { + order: 6 !important; + } + + .m-xxl-0 { + margin: 0 !important; + } + + .m-xxl-1 { + margin: 0.25rem !important; + } + + .m-xxl-2 { + margin: 0.5rem !important; + } + + .m-xxl-3 { + margin: 1rem !important; + } + + .m-xxl-4 { + margin: 1.5rem !important; + } + + .m-xxl-5 { + margin: 3rem !important; + } + + .m-xxl-auto { + margin: auto !important; + } + + .mx-xxl-0 { + margin-right: 0 !important; + margin-left: 0 !important; + } + + .mx-xxl-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + } + + .mx-xxl-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + } + + .mx-xxl-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; + } + + .mx-xxl-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + } + + .mx-xxl-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; + } + + .mx-xxl-auto { + margin-right: auto !important; + margin-left: auto !important; + } + + .my-xxl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xxl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xxl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xxl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xxl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xxl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xxl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xxl-0 { + margin-top: 0 !important; + } + + .mt-xxl-1 { + margin-top: 0.25rem !important; + } + + .mt-xxl-2 { + margin-top: 0.5rem !important; + } + + .mt-xxl-3 { + margin-top: 1rem !important; + } + + .mt-xxl-4 { + margin-top: 1.5rem !important; + } + + .mt-xxl-5 { + margin-top: 3rem !important; + } + + .mt-xxl-auto { + margin-top: auto !important; + } + + .me-xxl-0 { + margin-right: 0 !important; + } + + .me-xxl-1 { + margin-right: 0.25rem !important; + } + + .me-xxl-2 { + margin-right: 0.5rem !important; + } + + .me-xxl-3 { + margin-right: 1rem !important; + } + + .me-xxl-4 { + margin-right: 1.5rem !important; + } + + .me-xxl-5 { + margin-right: 3rem !important; + } + + .me-xxl-auto { + margin-right: auto !important; + } + + .mb-xxl-0 { + margin-bottom: 0 !important; + } + + .mb-xxl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xxl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xxl-3 { + margin-bottom: 1rem !important; + } + + .mb-xxl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xxl-5 { + margin-bottom: 3rem !important; + } + + .mb-xxl-auto { + margin-bottom: auto !important; + } + + .ms-xxl-0 { + margin-left: 0 !important; + } + + .ms-xxl-1 { + margin-left: 0.25rem !important; + } + + .ms-xxl-2 { + margin-left: 0.5rem !important; + } + + .ms-xxl-3 { + margin-left: 1rem !important; + } + + .ms-xxl-4 { + margin-left: 1.5rem !important; + } + + .ms-xxl-5 { + margin-left: 3rem !important; + } + + .ms-xxl-auto { + margin-left: auto !important; + } + + .p-xxl-0 { + padding: 0 !important; + } + + .p-xxl-1 { + padding: 0.25rem !important; + } + + .p-xxl-2 { + padding: 0.5rem !important; + } + + .p-xxl-3 { + padding: 1rem !important; + } + + .p-xxl-4 { + padding: 1.5rem !important; + } + + .p-xxl-5 { + padding: 3rem !important; + } + + .px-xxl-0 { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .px-xxl-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + } + + .px-xxl-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + } + + .px-xxl-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; + } + + .px-xxl-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + } + + .px-xxl-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; + } + + .py-xxl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xxl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xxl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xxl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xxl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xxl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xxl-0 { + padding-top: 0 !important; + } + + .pt-xxl-1 { + padding-top: 0.25rem !important; + } + + .pt-xxl-2 { + padding-top: 0.5rem !important; + } + + .pt-xxl-3 { + padding-top: 1rem !important; + } + + .pt-xxl-4 { + padding-top: 1.5rem !important; + } + + .pt-xxl-5 { + padding-top: 3rem !important; + } + + .pe-xxl-0 { + padding-right: 0 !important; + } + + .pe-xxl-1 { + padding-right: 0.25rem !important; + } + + .pe-xxl-2 { + padding-right: 0.5rem !important; + } + + .pe-xxl-3 { + padding-right: 1rem !important; + } + + .pe-xxl-4 { + padding-right: 1.5rem !important; + } + + .pe-xxl-5 { + padding-right: 3rem !important; + } + + .pb-xxl-0 { + padding-bottom: 0 !important; + } + + .pb-xxl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xxl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xxl-3 { + padding-bottom: 1rem !important; + } + + .pb-xxl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xxl-5 { + padding-bottom: 3rem !important; + } + + .ps-xxl-0 { + padding-left: 0 !important; + } + + .ps-xxl-1 { + padding-left: 0.25rem !important; + } + + .ps-xxl-2 { + padding-left: 0.5rem !important; + } + + .ps-xxl-3 { + padding-left: 1rem !important; + } + + .ps-xxl-4 { + padding-left: 1.5rem !important; + } + + .ps-xxl-5 { + padding-left: 3rem !important; + } + + .gap-xxl-0 { + gap: 0 !important; + } + + .gap-xxl-1 { + gap: 0.25rem !important; + } + + .gap-xxl-2 { + gap: 0.5rem !important; + } + + .gap-xxl-3 { + gap: 1rem !important; + } + + .gap-xxl-4 { + gap: 1.5rem !important; + } + + .gap-xxl-5 { + gap: 3rem !important; + } + + .row-gap-xxl-0 { + row-gap: 0 !important; + } + + .row-gap-xxl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xxl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xxl-3 { + row-gap: 1rem !important; + } + + .row-gap-xxl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xxl-5 { + row-gap: 3rem !important; + } + + .column-gap-xxl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xxl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xxl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xxl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xxl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xxl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xxl-start { + text-align: left !important; + } + + .text-xxl-end { + text-align: right !important; + } + + .text-xxl-center { + text-align: center !important; + } } + @media (min-width: 1200px) { - .fs-1 { - font-size: 2.5rem !important; - } - .fs-2 { - font-size: 2rem !important; - } - .fs-3 { - font-size: 1.75rem !important; - } - .fs-4 { - font-size: 1.5rem !important; - } + .fs-1 { + font-size: 2.5rem !important; + } + + .fs-2 { + font-size: 2rem !important; + } + + .fs-3 { + font-size: 1.75rem !important; + } + + .fs-4 { + font-size: 1.5rem !important; + } } + @media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-grid { + display: grid !important; + } + + .d-print-inline-grid { + display: inline-grid !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: flex !important; + } + + .d-print-inline-flex { + display: inline-flex !important; + } + + .d-print-none { + display: none !important; + } } -/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css index 753823c272..e04148d9b5 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css @@ -6,427 +6,435 @@ */ :root, [data-bs-theme=light] { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-black: #000; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #0d6efd; - --bs-secondary: #6c757d; - --bs-success: #198754; - --bs-info: #0dcaf0; - --bs-warning: #ffc107; - --bs-danger: #dc3545; - --bs-light: #f8f9fa; - --bs-dark: #212529; - --bs-primary-rgb: 13, 110, 253; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 25, 135, 84; - --bs-info-rgb: 13, 202, 240; - --bs-warning-rgb: 255, 193, 7; - --bs-danger-rgb: 220, 53, 69; - --bs-light-rgb: 248, 249, 250; - --bs-dark-rgb: 33, 37, 41; - --bs-primary-text-emphasis: #052c65; - --bs-secondary-text-emphasis: #2b2f32; - --bs-success-text-emphasis: #0a3622; - --bs-info-text-emphasis: #055160; - --bs-warning-text-emphasis: #664d03; - --bs-danger-text-emphasis: #58151c; - --bs-light-text-emphasis: #495057; - --bs-dark-text-emphasis: #495057; - --bs-primary-bg-subtle: #cfe2ff; - --bs-secondary-bg-subtle: #e2e3e5; - --bs-success-bg-subtle: #d1e7dd; - --bs-info-bg-subtle: #cff4fc; - --bs-warning-bg-subtle: #fff3cd; - --bs-danger-bg-subtle: #f8d7da; - --bs-light-bg-subtle: #fcfcfd; - --bs-dark-bg-subtle: #ced4da; - --bs-primary-border-subtle: #9ec5fe; - --bs-secondary-border-subtle: #c4c8cb; - --bs-success-border-subtle: #a3cfbb; - --bs-info-border-subtle: #9eeaf9; - --bs-warning-border-subtle: #ffe69c; - --bs-danger-border-subtle: #f1aeb5; - --bs-light-border-subtle: #e9ecef; - --bs-dark-border-subtle: #adb5bd; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-body-font-family: var(--bs-font-sans-serif); - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #212529; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg: #fff; - --bs-body-bg-rgb: 255, 255, 255; - --bs-emphasis-color: #000; - --bs-emphasis-color-rgb: 0, 0, 0; - --bs-secondary-color: rgba(33, 37, 41, 0.75); - --bs-secondary-color-rgb: 33, 37, 41; - --bs-secondary-bg: #e9ecef; - --bs-secondary-bg-rgb: 233, 236, 239; - --bs-tertiary-color: rgba(33, 37, 41, 0.5); - --bs-tertiary-color-rgb: 33, 37, 41; - --bs-tertiary-bg: #f8f9fa; - --bs-tertiary-bg-rgb: 248, 249, 250; - --bs-heading-color: inherit; - --bs-link-color: #0d6efd; - --bs-link-color-rgb: 13, 110, 253; - --bs-link-decoration: underline; - --bs-link-hover-color: #0a58ca; - --bs-link-hover-color-rgb: 10, 88, 202; - --bs-code-color: #d63384; - --bs-highlight-color: #212529; - --bs-highlight-bg: #fff3cd; - --bs-border-width: 1px; - --bs-border-style: solid; - --bs-border-color: #dee2e6; - --bs-border-color-translucent: rgba(0, 0, 0, 0.175); - --bs-border-radius: 0.375rem; - --bs-border-radius-sm: 0.25rem; - --bs-border-radius-lg: 0.5rem; - --bs-border-radius-xl: 1rem; - --bs-border-radius-xxl: 2rem; - --bs-border-radius-2xl: var(--bs-border-radius-xxl); - --bs-border-radius-pill: 50rem; - --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); - --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); - --bs-focus-ring-width: 0.25rem; - --bs-focus-ring-opacity: 0.25; - --bs-focus-ring-color: rgba(13, 110, 253, 0.25); - --bs-form-valid-color: #198754; - --bs-form-valid-border-color: #198754; - --bs-form-invalid-color: #dc3545; - --bs-form-invalid-border-color: #dc3545; + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-black: #000; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #0d6efd; + --bs-secondary: #6c757d; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-primary-rgb: 13, 110, 253; + --bs-secondary-rgb: 108, 117, 125; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-primary-text-emphasis: #052c65; + --bs-secondary-text-emphasis: #2b2f32; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #cfe2ff; + --bs-secondary-bg-subtle: #e2e3e5; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-primary-border-subtle: #9ec5fe; + --bs-secondary-border-subtle: #c4c8cb; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-body-font-family: var(--bs-font-sans-serif); + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #0d6efd; + --bs-link-color-rgb: 13, 110, 253; + --bs-link-decoration: underline; + --bs-link-hover-color: #0a58ca; + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-code-color: #d63384; + --bs-highlight-color: #212529; + --bs-highlight-bg: #fff3cd; + --bs-border-width: 1px; + --bs-border-style: solid; + --bs-border-color: #dee2e6; + --bs-border-color-translucent: rgba(0, 0, 0, 0.175); + --bs-border-radius: 0.375rem; + --bs-border-radius-sm: 0.25rem; + --bs-border-radius-lg: 0.5rem; + --bs-border-radius-xl: 1rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); + --bs-border-radius-pill: 50rem; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(13, 110, 253, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; } [data-bs-theme=dark] { - color-scheme: dark; - --bs-body-color: #dee2e6; - --bs-body-color-rgb: 222, 226, 230; - --bs-body-bg: #212529; - --bs-body-bg-rgb: 33, 37, 41; - --bs-emphasis-color: #fff; - --bs-emphasis-color-rgb: 255, 255, 255; - --bs-secondary-color: rgba(222, 226, 230, 0.75); - --bs-secondary-color-rgb: 222, 226, 230; - --bs-secondary-bg: #343a40; - --bs-secondary-bg-rgb: 52, 58, 64; - --bs-tertiary-color: rgba(222, 226, 230, 0.5); - --bs-tertiary-color-rgb: 222, 226, 230; - --bs-tertiary-bg: #2b3035; - --bs-tertiary-bg-rgb: 43, 48, 53; - --bs-primary-text-emphasis: #6ea8fe; - --bs-secondary-text-emphasis: #a7acb1; - --bs-success-text-emphasis: #75b798; - --bs-info-text-emphasis: #6edff6; - --bs-warning-text-emphasis: #ffda6a; - --bs-danger-text-emphasis: #ea868f; - --bs-light-text-emphasis: #f8f9fa; - --bs-dark-text-emphasis: #dee2e6; - --bs-primary-bg-subtle: #031633; - --bs-secondary-bg-subtle: #161719; - --bs-success-bg-subtle: #051b11; - --bs-info-bg-subtle: #032830; - --bs-warning-bg-subtle: #332701; - --bs-danger-bg-subtle: #2c0b0e; - --bs-light-bg-subtle: #343a40; - --bs-dark-bg-subtle: #1a1d20; - --bs-primary-border-subtle: #084298; - --bs-secondary-border-subtle: #41464b; - --bs-success-border-subtle: #0f5132; - --bs-info-border-subtle: #087990; - --bs-warning-border-subtle: #997404; - --bs-danger-border-subtle: #842029; - --bs-light-border-subtle: #495057; - --bs-dark-border-subtle: #343a40; - --bs-heading-color: inherit; - --bs-link-color: #6ea8fe; - --bs-link-hover-color: #8bb9fe; - --bs-link-color-rgb: 110, 168, 254; - --bs-link-hover-color-rgb: 139, 185, 254; - --bs-code-color: #e685b5; - --bs-highlight-color: #dee2e6; - --bs-highlight-bg: #664d03; - --bs-border-color: #495057; - --bs-border-color-translucent: rgba(255, 255, 255, 0.15); - --bs-form-valid-color: #75b798; - --bs-form-valid-border-color: #75b798; - --bs-form-invalid-color: #ea868f; - --bs-form-invalid-border-color: #ea868f; + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #6ea8fe; + --bs-secondary-text-emphasis: #a7acb1; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-primary-bg-subtle: #031633; + --bs-secondary-bg-subtle: #161719; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-primary-border-subtle: #084298; + --bs-secondary-border-subtle: #41464b; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-heading-color: inherit; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-code-color: #e685b5; + --bs-highlight-color: #dee2e6; + --bs-highlight-bg: #664d03; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; } *, *::before, *::after { - box-sizing: border-box; + box-sizing: border-box; } @media (prefers-reduced-motion: no-preference) { - :root { - scroll-behavior: smooth; - } + :root { + scroll-behavior: smooth; + } } body { - margin: 0; - font-family: var(--bs-body-font-family); - font-size: var(--bs-body-font-size); - font-weight: var(--bs-body-font-weight); - line-height: var(--bs-body-line-height); - color: var(--bs-body-color); - text-align: var(--bs-body-text-align); - background-color: var(--bs-body-bg); - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + margin: 0; + font-family: var(--bs-body-font-family); + font-size: var(--bs-body-font-size); + font-weight: var(--bs-body-font-weight); + line-height: var(--bs-body-line-height); + color: var(--bs-body-color); + text-align: var(--bs-body-text-align); + background-color: var(--bs-body-bg); + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } hr { - margin: 1rem 0; - color: inherit; - border: 0; - border-top: var(--bs-border-width) solid; - opacity: 0.25; + margin: 1rem 0; + color: inherit; + border: 0; + border-top: var(--bs-border-width) solid; + opacity: 0.25; } h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { - margin-top: 0; - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; - color: var(--bs-heading-color); + margin-top: 0; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; + color: var(--bs-heading-color); } h1, .h1 { - font-size: calc(1.375rem + 1.5vw); + font-size: calc(1.375rem + 1.5vw); } + @media (min-width: 1200px) { - h1, .h1 { - font-size: 2.5rem; - } + h1, .h1 { + font-size: 2.5rem; + } } h2, .h2 { - font-size: calc(1.325rem + 0.9vw); + font-size: calc(1.325rem + 0.9vw); } + @media (min-width: 1200px) { - h2, .h2 { - font-size: 2rem; - } + h2, .h2 { + font-size: 2rem; + } } h3, .h3 { - font-size: calc(1.3rem + 0.6vw); + font-size: calc(1.3rem + 0.6vw); } + @media (min-width: 1200px) { - h3, .h3 { - font-size: 1.75rem; - } + h3, .h3 { + font-size: 1.75rem; + } } h4, .h4 { - font-size: calc(1.275rem + 0.3vw); + font-size: calc(1.275rem + 0.3vw); } + @media (min-width: 1200px) { - h4, .h4 { - font-size: 1.5rem; - } + h4, .h4 { + font-size: 1.5rem; + } } h5, .h5 { - font-size: 1.25rem; + font-size: 1.25rem; } h6, .h6 { - font-size: 1rem; + font-size: 1rem; } p { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } abbr[title] { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none; } address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; } ol, ul { - padding-right: 2rem; + padding-right: 2rem; } ol, ul, dl { - margin-top: 0; - margin-bottom: 1rem; + margin-top: 0; + margin-bottom: 1rem; } ol ol, ul ul, ol ul, ul ol { - margin-bottom: 0; + margin-bottom: 0; } dt { - font-weight: 700; + font-weight: 700; } dd { - margin-bottom: 0.5rem; - margin-right: 0; + margin-bottom: 0.5rem; + margin-right: 0; } blockquote { - margin: 0 0 1rem; + margin: 0 0 1rem; } b, strong { - font-weight: bolder; + font-weight: bolder; } small, .small { - font-size: 0.875em; + font-size: 0.875em; } mark, .mark { - padding: 0.1875em; - color: var(--bs-highlight-color); - background-color: var(--bs-highlight-bg); + padding: 0.1875em; + color: var(--bs-highlight-color); + background-color: var(--bs-highlight-bg); } sub, sup { - position: relative; - font-size: 0.75em; - line-height: 0; - vertical-align: baseline; + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; } sub { - bottom: -0.25em; + bottom: -0.25em; } sup { - top: -0.5em; + top: -0.5em; } a { - color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); - text-decoration: underline; + color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); + text-decoration: underline; } + a:hover { - --bs-link-color-rgb: var(--bs-link-hover-color-rgb); + --bs-link-color-rgb: var(--bs-link-hover-color-rgb); } a:not([href]):not([class]), a:not([href]):not([class]):hover { - color: inherit; - text-decoration: none; + color: inherit; + text-decoration: none; } pre, code, kbd, samp { - font-family: var(--bs-font-monospace); - font-size: 1em; + font-family: var(--bs-font-monospace); + font-size: 1em; } pre { - display: block; - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - font-size: 0.875em; + display: block; + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + font-size: 0.875em; } + pre code { - font-size: inherit; - color: inherit; - word-break: normal; + font-size: inherit; + color: inherit; + word-break: normal; } code { - font-size: 0.875em; - color: var(--bs-code-color); - word-wrap: break-word; + font-size: 0.875em; + color: var(--bs-code-color); + word-wrap: break-word; } + a > code { - color: inherit; + color: inherit; } kbd { - padding: 0.1875rem 0.375rem; - font-size: 0.875em; - color: var(--bs-body-bg); - background-color: var(--bs-body-color); - border-radius: 0.25rem; + padding: 0.1875rem 0.375rem; + font-size: 0.875em; + color: var(--bs-body-bg); + background-color: var(--bs-body-color); + border-radius: 0.25rem; } + kbd kbd { - padding: 0; - font-size: 1em; + padding: 0; + font-size: 1em; } figure { - margin: 0 0 1rem; + margin: 0 0 1rem; } img, svg { - vertical-align: middle; + vertical-align: middle; } table { - caption-side: bottom; - border-collapse: collapse; + caption-side: bottom; + border-collapse: collapse; } caption { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: var(--bs-secondary-color); - text-align: right; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-secondary-color); + text-align: right; } th { - text-align: inherit; - text-align: -webkit-match-parent; + text-align: inherit; + text-align: -webkit-match-parent; } thead, @@ -435,21 +443,21 @@ tfoot, tr, td, th { - border-color: inherit; - border-style: solid; - border-width: 0; + border-color: inherit; + border-style: solid; + border-width: 0; } label { - display: inline-block; + display: inline-block; } button { - border-radius: 0; + border-radius: 0; } button:focus:not(:focus-visible) { - outline: 0; + outline: 0; } input, @@ -457,76 +465,80 @@ button, select, optgroup, textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; } button, select { - text-transform: none; + text-transform: none; } [role=button] { - cursor: pointer; + cursor: pointer; } select { - word-wrap: normal; + word-wrap: normal; } + select:disabled { - opacity: 1; + opacity: 1; } [list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { - display: none !important; + display: none !important; } button, [type=button], [type=reset], [type=submit] { - -webkit-appearance: button; + -webkit-appearance: button; } + button:not(:disabled), [type=button]:not(:disabled), [type=reset]:not(:disabled), [type=submit]:not(:disabled) { - cursor: pointer; + cursor: pointer; } ::-moz-focus-inner { - padding: 0; - border-style: none; + padding: 0; + border-style: none; } textarea { - resize: vertical; + resize: vertical; } fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; + min-width: 0; + padding: 0; + margin: 0; + border: 0; } legend { - float: right; - width: 100%; - padding: 0; - margin-bottom: 0.5rem; - font-size: calc(1.275rem + 0.3vw); - line-height: inherit; + float: right; + width: 100%; + padding: 0; + margin-bottom: 0.5rem; + font-size: calc(1.275rem + 0.3vw); + line-height: inherit; } + @media (min-width: 1200px) { - legend { - font-size: 1.5rem; - } + legend { + font-size: 1.5rem; + } } + legend + * { - clear: right; + clear: right; } ::-webkit-datetime-edit-fields-wrapper, @@ -536,200 +548,210 @@ legend + * { ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-year-field { - padding: 0; + padding: 0; } ::-webkit-inner-spin-button { - height: auto; + height: auto; } [type=search] { - -webkit-appearance: textfield; - outline-offset: -2px; + -webkit-appearance: textfield; + outline-offset: -2px; } [type="tel"], [type="url"], [type="email"], [type="number"] { - direction: ltr; + direction: ltr; } + ::-webkit-search-decoration { - -webkit-appearance: none; + -webkit-appearance: none; } ::-webkit-color-swatch-wrapper { - padding: 0; + padding: 0; } ::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } ::file-selector-button { - font: inherit; - -webkit-appearance: button; + font: inherit; + -webkit-appearance: button; } output { - display: inline-block; + display: inline-block; } iframe { - border: 0; + border: 0; } summary { - display: list-item; - cursor: pointer; + display: list-item; + cursor: pointer; } progress { - vertical-align: baseline; + vertical-align: baseline; } [hidden] { - display: none !important; + display: none !important; } .lead { - font-size: 1.25rem; - font-weight: 300; + font-size: 1.25rem; + font-weight: 300; } .display-1 { - font-size: calc(1.625rem + 4.5vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.625rem + 4.5vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-1 { - font-size: 5rem; - } + .display-1 { + font-size: 5rem; + } } .display-2 { - font-size: calc(1.575rem + 3.9vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.575rem + 3.9vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-2 { - font-size: 4.5rem; - } + .display-2 { + font-size: 4.5rem; + } } .display-3 { - font-size: calc(1.525rem + 3.3vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.525rem + 3.3vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-3 { - font-size: 4rem; - } + .display-3 { + font-size: 4rem; + } } .display-4 { - font-size: calc(1.475rem + 2.7vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.475rem + 2.7vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-4 { - font-size: 3.5rem; - } + .display-4 { + font-size: 3.5rem; + } } .display-5 { - font-size: calc(1.425rem + 2.1vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.425rem + 2.1vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-5 { - font-size: 3rem; - } + .display-5 { + font-size: 3rem; + } } .display-6 { - font-size: calc(1.375rem + 1.5vw); - font-weight: 300; - line-height: 1.2; + font-size: calc(1.375rem + 1.5vw); + font-weight: 300; + line-height: 1.2; } + @media (min-width: 1200px) { - .display-6 { - font-size: 2.5rem; - } + .display-6 { + font-size: 2.5rem; + } } .list-unstyled { - padding-right: 0; - list-style: none; + padding-right: 0; + list-style: none; } .list-inline { - padding-right: 0; - list-style: none; + padding-right: 0; + list-style: none; } .list-inline-item { - display: inline-block; + display: inline-block; } + .list-inline-item:not(:last-child) { - margin-left: 0.5rem; + margin-left: 0.5rem; } .initialism { - font-size: 0.875em; - text-transform: uppercase; + font-size: 0.875em; + text-transform: uppercase; } .blockquote { - margin-bottom: 1rem; - font-size: 1.25rem; + margin-bottom: 1rem; + font-size: 1.25rem; } + .blockquote > :last-child { - margin-bottom: 0; + margin-bottom: 0; } .blockquote-footer { - margin-top: -1rem; - margin-bottom: 1rem; - font-size: 0.875em; - color: #6c757d; + margin-top: -1rem; + margin-bottom: 1rem; + font-size: 0.875em; + color: #6c757d; } + .blockquote-footer::before { - content: "— "; + content: "— "; } .img-fluid { - max-width: 100%; - height: auto; + max-width: 100%; + height: auto; } .img-thumbnail { - padding: 0.25rem; - background-color: var(--bs-body-bg); - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); - max-width: 100%; - height: auto; + padding: 0.25rem; + background-color: var(--bs-body-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + max-width: 100%; + height: auto; } .figure { - display: inline-block; + display: inline-block; } .figure-img { - margin-bottom: 0.5rem; - line-height: 1; + margin-bottom: 0.5rem; + line-height: 1; } .figure-caption { - font-size: 0.875em; - color: var(--bs-secondary-color); + font-size: 0.875em; + color: var(--bs-secondary-color); } .container, @@ -739,2137 +761,2476 @@ progress { .container-lg, .container-md, .container-sm { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - width: 100%; - padding-left: calc(var(--bs-gutter-x) * 0.5); - padding-right: calc(var(--bs-gutter-x) * 0.5); - margin-left: auto; - margin-right: auto; + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + width: 100%; + padding-left: calc(var(--bs-gutter-x) * 0.5); + padding-right: calc(var(--bs-gutter-x) * 0.5); + margin-left: auto; + margin-right: auto; } @media (min-width: 576px) { - .container-sm, .container { - max-width: 540px; - } + .container-sm, .container { + max-width: 540px; + } } + @media (min-width: 768px) { - .container-md, .container-sm, .container { - max-width: 720px; - } + .container-md, .container-sm, .container { + max-width: 720px; + } } + @media (min-width: 992px) { - .container-lg, .container-md, .container-sm, .container { - max-width: 960px; - } + .container-lg, .container-md, .container-sm, .container { + max-width: 960px; + } } + @media (min-width: 1200px) { - .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1140px; - } + .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1140px; + } } + @media (min-width: 1400px) { - .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1320px; - } + .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1320px; + } } + :root { - --bs-breakpoint-xs: 0; - --bs-breakpoint-sm: 576px; - --bs-breakpoint-md: 768px; - --bs-breakpoint-lg: 992px; - --bs-breakpoint-xl: 1200px; - --bs-breakpoint-xxl: 1400px; + --bs-breakpoint-xs: 0; + --bs-breakpoint-sm: 576px; + --bs-breakpoint-md: 768px; + --bs-breakpoint-lg: 992px; + --bs-breakpoint-xl: 1200px; + --bs-breakpoint-xxl: 1400px; } .row { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - display: flex; - flex-wrap: wrap; - margin-top: calc(-1 * var(--bs-gutter-y)); - margin-left: calc(-0.5 * var(--bs-gutter-x)); - margin-right: calc(-0.5 * var(--bs-gutter-x)); + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + display: flex; + flex-wrap: wrap; + margin-top: calc(-1 * var(--bs-gutter-y)); + margin-left: calc(-0.5 * var(--bs-gutter-x)); + margin-right: calc(-0.5 * var(--bs-gutter-x)); } + .row > * { - flex-shrink: 0; - width: 100%; - max-width: 100%; - padding-left: calc(var(--bs-gutter-x) * 0.5); - padding-right: calc(var(--bs-gutter-x) * 0.5); - margin-top: var(--bs-gutter-y); + flex-shrink: 0; + width: 100%; + max-width: 100%; + padding-left: calc(var(--bs-gutter-x) * 0.5); + padding-right: calc(var(--bs-gutter-x) * 0.5); + margin-top: var(--bs-gutter-y); } .col { - flex: 1 0 0%; + flex: 1 0 0%; } .row-cols-auto > * { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .row-cols-1 > * { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .row-cols-2 > * { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .row-cols-3 > * { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .row-cols-4 > * { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .row-cols-5 > * { - flex: 0 0 auto; - width: 20%; + flex: 0 0 auto; + width: 20%; } .row-cols-6 > * { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-auto { - flex: 0 0 auto; - width: auto; + flex: 0 0 auto; + width: auto; } .col-1 { - flex: 0 0 auto; - width: 8.33333333%; + flex: 0 0 auto; + width: 8.33333333%; } .col-2 { - flex: 0 0 auto; - width: 16.66666667%; + flex: 0 0 auto; + width: 16.66666667%; } .col-3 { - flex: 0 0 auto; - width: 25%; + flex: 0 0 auto; + width: 25%; } .col-4 { - flex: 0 0 auto; - width: 33.33333333%; + flex: 0 0 auto; + width: 33.33333333%; } .col-5 { - flex: 0 0 auto; - width: 41.66666667%; + flex: 0 0 auto; + width: 41.66666667%; } .col-6 { - flex: 0 0 auto; - width: 50%; + flex: 0 0 auto; + width: 50%; } .col-7 { - flex: 0 0 auto; - width: 58.33333333%; + flex: 0 0 auto; + width: 58.33333333%; } .col-8 { - flex: 0 0 auto; - width: 66.66666667%; + flex: 0 0 auto; + width: 66.66666667%; } .col-9 { - flex: 0 0 auto; - width: 75%; + flex: 0 0 auto; + width: 75%; } .col-10 { - flex: 0 0 auto; - width: 83.33333333%; + flex: 0 0 auto; + width: 83.33333333%; } .col-11 { - flex: 0 0 auto; - width: 91.66666667%; + flex: 0 0 auto; + width: 91.66666667%; } .col-12 { - flex: 0 0 auto; - width: 100%; + flex: 0 0 auto; + width: 100%; } .offset-1 { - margin-right: 8.33333333%; + margin-right: 8.33333333%; } .offset-2 { - margin-right: 16.66666667%; + margin-right: 16.66666667%; } .offset-3 { - margin-right: 25%; + margin-right: 25%; } .offset-4 { - margin-right: 33.33333333%; + margin-right: 33.33333333%; } .offset-5 { - margin-right: 41.66666667%; + margin-right: 41.66666667%; } .offset-6 { - margin-right: 50%; + margin-right: 50%; } .offset-7 { - margin-right: 58.33333333%; + margin-right: 58.33333333%; } .offset-8 { - margin-right: 66.66666667%; + margin-right: 66.66666667%; } .offset-9 { - margin-right: 75%; + margin-right: 75%; } .offset-10 { - margin-right: 83.33333333%; + margin-right: 83.33333333%; } .offset-11 { - margin-right: 91.66666667%; + margin-right: 91.66666667%; } .g-0, .gx-0 { - --bs-gutter-x: 0; + --bs-gutter-x: 0; } .g-0, .gy-0 { - --bs-gutter-y: 0; + --bs-gutter-y: 0; } .g-1, .gx-1 { - --bs-gutter-x: 0.25rem; + --bs-gutter-x: 0.25rem; } .g-1, .gy-1 { - --bs-gutter-y: 0.25rem; + --bs-gutter-y: 0.25rem; } .g-2, .gx-2 { - --bs-gutter-x: 0.5rem; + --bs-gutter-x: 0.5rem; } .g-2, .gy-2 { - --bs-gutter-y: 0.5rem; + --bs-gutter-y: 0.5rem; } .g-3, .gx-3 { - --bs-gutter-x: 1rem; + --bs-gutter-x: 1rem; } .g-3, .gy-3 { - --bs-gutter-y: 1rem; + --bs-gutter-y: 1rem; } .g-4, .gx-4 { - --bs-gutter-x: 1.5rem; + --bs-gutter-x: 1.5rem; } .g-4, .gy-4 { - --bs-gutter-y: 1.5rem; + --bs-gutter-y: 1.5rem; } .g-5, .gx-5 { - --bs-gutter-x: 3rem; + --bs-gutter-x: 3rem; } .g-5, .gy-5 { - --bs-gutter-y: 3rem; + --bs-gutter-y: 3rem; } @media (min-width: 576px) { - .col-sm { - flex: 1 0 0%; - } - .row-cols-sm-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-sm-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-sm-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-sm-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-sm-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-sm-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-sm-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-auto { - flex: 0 0 auto; - width: auto; - } - .col-sm-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-sm-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-3 { - flex: 0 0 auto; - width: 25%; - } - .col-sm-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-sm-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-sm-6 { - flex: 0 0 auto; - width: 50%; - } - .col-sm-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-sm-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-sm-9 { - flex: 0 0 auto; - width: 75%; - } - .col-sm-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-sm-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-sm-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-sm-0 { - margin-right: 0; - } - .offset-sm-1 { - margin-right: 8.33333333%; - } - .offset-sm-2 { - margin-right: 16.66666667%; - } - .offset-sm-3 { - margin-right: 25%; - } - .offset-sm-4 { - margin-right: 33.33333333%; - } - .offset-sm-5 { - margin-right: 41.66666667%; - } - .offset-sm-6 { - margin-right: 50%; - } - .offset-sm-7 { - margin-right: 58.33333333%; - } - .offset-sm-8 { - margin-right: 66.66666667%; - } - .offset-sm-9 { - margin-right: 75%; - } - .offset-sm-10 { - margin-right: 83.33333333%; - } - .offset-sm-11 { - margin-right: 91.66666667%; - } - .g-sm-0, - .gx-sm-0 { - --bs-gutter-x: 0; - } - .g-sm-0, - .gy-sm-0 { - --bs-gutter-y: 0; - } - .g-sm-1, - .gx-sm-1 { - --bs-gutter-x: 0.25rem; - } - .g-sm-1, - .gy-sm-1 { - --bs-gutter-y: 0.25rem; - } - .g-sm-2, - .gx-sm-2 { - --bs-gutter-x: 0.5rem; - } - .g-sm-2, - .gy-sm-2 { - --bs-gutter-y: 0.5rem; - } - .g-sm-3, - .gx-sm-3 { - --bs-gutter-x: 1rem; - } - .g-sm-3, - .gy-sm-3 { - --bs-gutter-y: 1rem; - } - .g-sm-4, - .gx-sm-4 { - --bs-gutter-x: 1.5rem; - } - .g-sm-4, - .gy-sm-4 { - --bs-gutter-y: 1.5rem; - } - .g-sm-5, - .gx-sm-5 { - --bs-gutter-x: 3rem; - } - .g-sm-5, - .gy-sm-5 { - --bs-gutter-y: 3rem; - } + .col-sm { + flex: 1 0 0%; + } + + .row-cols-sm-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-sm-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-sm-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-sm-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-sm-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-sm-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-sm-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + } + + .col-sm-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-sm-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-sm-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-sm-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-sm-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-sm-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-sm-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-sm-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-sm-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-sm-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-sm-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-sm-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-sm-0 { + margin-right: 0; + } + + .offset-sm-1 { + margin-right: 8.33333333%; + } + + .offset-sm-2 { + margin-right: 16.66666667%; + } + + .offset-sm-3 { + margin-right: 25%; + } + + .offset-sm-4 { + margin-right: 33.33333333%; + } + + .offset-sm-5 { + margin-right: 41.66666667%; + } + + .offset-sm-6 { + margin-right: 50%; + } + + .offset-sm-7 { + margin-right: 58.33333333%; + } + + .offset-sm-8 { + margin-right: 66.66666667%; + } + + .offset-sm-9 { + margin-right: 75%; + } + + .offset-sm-10 { + margin-right: 83.33333333%; + } + + .offset-sm-11 { + margin-right: 91.66666667%; + } + + .g-sm-0, + .gx-sm-0 { + --bs-gutter-x: 0; + } + + .g-sm-0, + .gy-sm-0 { + --bs-gutter-y: 0; + } + + .g-sm-1, + .gx-sm-1 { + --bs-gutter-x: 0.25rem; + } + + .g-sm-1, + .gy-sm-1 { + --bs-gutter-y: 0.25rem; + } + + .g-sm-2, + .gx-sm-2 { + --bs-gutter-x: 0.5rem; + } + + .g-sm-2, + .gy-sm-2 { + --bs-gutter-y: 0.5rem; + } + + .g-sm-3, + .gx-sm-3 { + --bs-gutter-x: 1rem; + } + + .g-sm-3, + .gy-sm-3 { + --bs-gutter-y: 1rem; + } + + .g-sm-4, + .gx-sm-4 { + --bs-gutter-x: 1.5rem; + } + + .g-sm-4, + .gy-sm-4 { + --bs-gutter-y: 1.5rem; + } + + .g-sm-5, + .gx-sm-5 { + --bs-gutter-x: 3rem; + } + + .g-sm-5, + .gy-sm-5 { + --bs-gutter-y: 3rem; + } } + @media (min-width: 768px) { - .col-md { - flex: 1 0 0%; - } - .row-cols-md-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-md-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-md-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-md-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-md-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-md-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-md-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-auto { - flex: 0 0 auto; - width: auto; - } - .col-md-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-md-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-3 { - flex: 0 0 auto; - width: 25%; - } - .col-md-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-md-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-md-6 { - flex: 0 0 auto; - width: 50%; - } - .col-md-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-md-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-md-9 { - flex: 0 0 auto; - width: 75%; - } - .col-md-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-md-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-md-12 { - flex: 0 0 auto; + .col-md { + flex: 1 0 0%; + } + + .row-cols-md-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-md-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-md-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-md-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-md-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-md-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-md-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + } + + .col-md-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-md-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-md-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-md-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-md-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-md-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-md-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-md-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-md-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-md-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-md-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-md-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-md-0 { + margin-right: 0; + } + + .offset-md-1 { + margin-right: 8.33333333%; + } + + .offset-md-2 { + margin-right: 16.66666667%; + } + + .offset-md-3 { + margin-right: 25%; + } + + .offset-md-4 { + margin-right: 33.33333333%; + } + + .offset-md-5 { + margin-right: 41.66666667%; + } + + .offset-md-6 { + margin-right: 50%; + } + + .offset-md-7 { + margin-right: 58.33333333%; + } + + .offset-md-8 { + margin-right: 66.66666667%; + } + + .offset-md-9 { + margin-right: 75%; + } + + .offset-md-10 { + margin-right: 83.33333333%; + } + + .offset-md-11 { + margin-right: 91.66666667%; + } + + .g-md-0, + .gx-md-0 { + --bs-gutter-x: 0; + } + + .g-md-0, + .gy-md-0 { + --bs-gutter-y: 0; + } + + .g-md-1, + .gx-md-1 { + --bs-gutter-x: 0.25rem; + } + + .g-md-1, + .gy-md-1 { + --bs-gutter-y: 0.25rem; + } + + .g-md-2, + .gx-md-2 { + --bs-gutter-x: 0.5rem; + } + + .g-md-2, + .gy-md-2 { + --bs-gutter-y: 0.5rem; + } + + .g-md-3, + .gx-md-3 { + --bs-gutter-x: 1rem; + } + + .g-md-3, + .gy-md-3 { + --bs-gutter-y: 1rem; + } + + .g-md-4, + .gx-md-4 { + --bs-gutter-x: 1.5rem; + } + + .g-md-4, + .gy-md-4 { + --bs-gutter-y: 1.5rem; + } + + .g-md-5, + .gx-md-5 { + --bs-gutter-x: 3rem; + } + + .g-md-5, + .gy-md-5 { + --bs-gutter-y: 3rem; + } +} + +@media (min-width: 992px) { + .col-lg { + flex: 1 0 0%; + } + + .row-cols-lg-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-lg-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-lg-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-lg-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-lg-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-lg-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-lg-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + } + + .col-lg-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-lg-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-lg-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-lg-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-lg-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-lg-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-lg-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-lg-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-lg-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-lg-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-lg-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-lg-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-lg-0 { + margin-right: 0; + } + + .offset-lg-1 { + margin-right: 8.33333333%; + } + + .offset-lg-2 { + margin-right: 16.66666667%; + } + + .offset-lg-3 { + margin-right: 25%; + } + + .offset-lg-4 { + margin-right: 33.33333333%; + } + + .offset-lg-5 { + margin-right: 41.66666667%; + } + + .offset-lg-6 { + margin-right: 50%; + } + + .offset-lg-7 { + margin-right: 58.33333333%; + } + + .offset-lg-8 { + margin-right: 66.66666667%; + } + + .offset-lg-9 { + margin-right: 75%; + } + + .offset-lg-10 { + margin-right: 83.33333333%; + } + + .offset-lg-11 { + margin-right: 91.66666667%; + } + + .g-lg-0, + .gx-lg-0 { + --bs-gutter-x: 0; + } + + .g-lg-0, + .gy-lg-0 { + --bs-gutter-y: 0; + } + + .g-lg-1, + .gx-lg-1 { + --bs-gutter-x: 0.25rem; + } + + .g-lg-1, + .gy-lg-1 { + --bs-gutter-y: 0.25rem; + } + + .g-lg-2, + .gx-lg-2 { + --bs-gutter-x: 0.5rem; + } + + .g-lg-2, + .gy-lg-2 { + --bs-gutter-y: 0.5rem; + } + + .g-lg-3, + .gx-lg-3 { + --bs-gutter-x: 1rem; + } + + .g-lg-3, + .gy-lg-3 { + --bs-gutter-y: 1rem; + } + + .g-lg-4, + .gx-lg-4 { + --bs-gutter-x: 1.5rem; + } + + .g-lg-4, + .gy-lg-4 { + --bs-gutter-y: 1.5rem; + } + + .g-lg-5, + .gx-lg-5 { + --bs-gutter-x: 3rem; + } + + .g-lg-5, + .gy-lg-5 { + --bs-gutter-y: 3rem; + } +} + +@media (min-width: 1200px) { + .col-xl { + flex: 1 0 0%; + } + + .row-cols-xl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xl-0 { + margin-right: 0; + } + + .offset-xl-1 { + margin-right: 8.33333333%; + } + + .offset-xl-2 { + margin-right: 16.66666667%; + } + + .offset-xl-3 { + margin-right: 25%; + } + + .offset-xl-4 { + margin-right: 33.33333333%; + } + + .offset-xl-5 { + margin-right: 41.66666667%; + } + + .offset-xl-6 { + margin-right: 50%; + } + + .offset-xl-7 { + margin-right: 58.33333333%; + } + + .offset-xl-8 { + margin-right: 66.66666667%; + } + + .offset-xl-9 { + margin-right: 75%; + } + + .offset-xl-10 { + margin-right: 83.33333333%; + } + + .offset-xl-11 { + margin-right: 91.66666667%; + } + + .g-xl-0, + .gx-xl-0 { + --bs-gutter-x: 0; + } + + .g-xl-0, + .gy-xl-0 { + --bs-gutter-y: 0; + } + + .g-xl-1, + .gx-xl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xl-1, + .gy-xl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xl-2, + .gx-xl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xl-2, + .gy-xl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xl-3, + .gx-xl-3 { + --bs-gutter-x: 1rem; + } + + .g-xl-3, + .gy-xl-3 { + --bs-gutter-y: 1rem; + } + + .g-xl-4, + .gx-xl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xl-4, + .gy-xl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xl-5, + .gx-xl-5 { + --bs-gutter-x: 3rem; + } + + .g-xl-5, + .gy-xl-5 { + --bs-gutter-y: 3rem; + } +} + +@media (min-width: 1400px) { + .col-xxl { + flex: 1 0 0%; + } + + .row-cols-xxl-auto > * { + flex: 0 0 auto; + width: auto; + } + + .row-cols-xxl-1 > * { + flex: 0 0 auto; + width: 100%; + } + + .row-cols-xxl-2 > * { + flex: 0 0 auto; + width: 50%; + } + + .row-cols-xxl-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + + .row-cols-xxl-4 > * { + flex: 0 0 auto; + width: 25%; + } + + .row-cols-xxl-5 > * { + flex: 0 0 auto; + width: 20%; + } + + .row-cols-xxl-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-auto { + flex: 0 0 auto; + width: auto; + } + + .col-xxl-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + + .col-xxl-2 { + flex: 0 0 auto; + width: 16.66666667%; + } + + .col-xxl-3 { + flex: 0 0 auto; + width: 25%; + } + + .col-xxl-4 { + flex: 0 0 auto; + width: 33.33333333%; + } + + .col-xxl-5 { + flex: 0 0 auto; + width: 41.66666667%; + } + + .col-xxl-6 { + flex: 0 0 auto; + width: 50%; + } + + .col-xxl-7 { + flex: 0 0 auto; + width: 58.33333333%; + } + + .col-xxl-8 { + flex: 0 0 auto; + width: 66.66666667%; + } + + .col-xxl-9 { + flex: 0 0 auto; + width: 75%; + } + + .col-xxl-10 { + flex: 0 0 auto; + width: 83.33333333%; + } + + .col-xxl-11 { + flex: 0 0 auto; + width: 91.66666667%; + } + + .col-xxl-12 { + flex: 0 0 auto; + width: 100%; + } + + .offset-xxl-0 { + margin-right: 0; + } + + .offset-xxl-1 { + margin-right: 8.33333333%; + } + + .offset-xxl-2 { + margin-right: 16.66666667%; + } + + .offset-xxl-3 { + margin-right: 25%; + } + + .offset-xxl-4 { + margin-right: 33.33333333%; + } + + .offset-xxl-5 { + margin-right: 41.66666667%; + } + + .offset-xxl-6 { + margin-right: 50%; + } + + .offset-xxl-7 { + margin-right: 58.33333333%; + } + + .offset-xxl-8 { + margin-right: 66.66666667%; + } + + .offset-xxl-9 { + margin-right: 75%; + } + + .offset-xxl-10 { + margin-right: 83.33333333%; + } + + .offset-xxl-11 { + margin-right: 91.66666667%; + } + + .g-xxl-0, + .gx-xxl-0 { + --bs-gutter-x: 0; + } + + .g-xxl-0, + .gy-xxl-0 { + --bs-gutter-y: 0; + } + + .g-xxl-1, + .gx-xxl-1 { + --bs-gutter-x: 0.25rem; + } + + .g-xxl-1, + .gy-xxl-1 { + --bs-gutter-y: 0.25rem; + } + + .g-xxl-2, + .gx-xxl-2 { + --bs-gutter-x: 0.5rem; + } + + .g-xxl-2, + .gy-xxl-2 { + --bs-gutter-y: 0.5rem; + } + + .g-xxl-3, + .gx-xxl-3 { + --bs-gutter-x: 1rem; + } + + .g-xxl-3, + .gy-xxl-3 { + --bs-gutter-y: 1rem; + } + + .g-xxl-4, + .gx-xxl-4 { + --bs-gutter-x: 1.5rem; + } + + .g-xxl-4, + .gy-xxl-4 { + --bs-gutter-y: 1.5rem; + } + + .g-xxl-5, + .gx-xxl-5 { + --bs-gutter-x: 3rem; + } + + .g-xxl-5, + .gy-xxl-5 { + --bs-gutter-y: 3rem; + } +} + +.table { + --bs-table-color-type: initial; + --bs-table-bg-type: initial; + --bs-table-color-state: initial; + --bs-table-bg-state: initial; + --bs-table-color: var(--bs-emphasis-color); + --bs-table-bg: var(--bs-body-bg); + --bs-table-border-color: var(--bs-border-color); + --bs-table-accent-bg: transparent; + --bs-table-striped-color: var(--bs-emphasis-color); + --bs-table-striped-bg: rgba(var(--bs-emphasis-color-rgb), 0.05); + --bs-table-active-color: var(--bs-emphasis-color); + --bs-table-active-bg: rgba(var(--bs-emphasis-color-rgb), 0.1); + --bs-table-hover-color: var(--bs-emphasis-color); + --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); width: 100%; - } - .offset-md-0 { - margin-right: 0; - } - .offset-md-1 { - margin-right: 8.33333333%; - } - .offset-md-2 { - margin-right: 16.66666667%; - } - .offset-md-3 { - margin-right: 25%; - } - .offset-md-4 { - margin-right: 33.33333333%; - } - .offset-md-5 { - margin-right: 41.66666667%; - } - .offset-md-6 { - margin-right: 50%; - } - .offset-md-7 { - margin-right: 58.33333333%; - } - .offset-md-8 { - margin-right: 66.66666667%; - } - .offset-md-9 { - margin-right: 75%; - } - .offset-md-10 { - margin-right: 83.33333333%; - } - .offset-md-11 { - margin-right: 91.66666667%; - } - .g-md-0, - .gx-md-0 { - --bs-gutter-x: 0; - } - .g-md-0, - .gy-md-0 { - --bs-gutter-y: 0; - } - .g-md-1, - .gx-md-1 { - --bs-gutter-x: 0.25rem; - } - .g-md-1, - .gy-md-1 { - --bs-gutter-y: 0.25rem; - } - .g-md-2, - .gx-md-2 { - --bs-gutter-x: 0.5rem; - } - .g-md-2, - .gy-md-2 { - --bs-gutter-y: 0.5rem; - } - .g-md-3, - .gx-md-3 { - --bs-gutter-x: 1rem; - } - .g-md-3, - .gy-md-3 { - --bs-gutter-y: 1rem; - } - .g-md-4, - .gx-md-4 { - --bs-gutter-x: 1.5rem; - } - .g-md-4, - .gy-md-4 { - --bs-gutter-y: 1.5rem; - } - .g-md-5, - .gx-md-5 { - --bs-gutter-x: 3rem; - } - .g-md-5, - .gy-md-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 992px) { - .col-lg { - flex: 1 0 0%; - } - .row-cols-lg-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-lg-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-lg-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-lg-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-lg-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-lg-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-lg-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-auto { - flex: 0 0 auto; - width: auto; - } - .col-lg-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-lg-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-3 { - flex: 0 0 auto; - width: 25%; - } - .col-lg-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-lg-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-lg-6 { - flex: 0 0 auto; - width: 50%; - } - .col-lg-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-lg-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-lg-9 { - flex: 0 0 auto; - width: 75%; - } - .col-lg-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-lg-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-lg-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-lg-0 { - margin-right: 0; - } - .offset-lg-1 { - margin-right: 8.33333333%; - } - .offset-lg-2 { - margin-right: 16.66666667%; - } - .offset-lg-3 { - margin-right: 25%; - } - .offset-lg-4 { - margin-right: 33.33333333%; - } - .offset-lg-5 { - margin-right: 41.66666667%; - } - .offset-lg-6 { - margin-right: 50%; - } - .offset-lg-7 { - margin-right: 58.33333333%; - } - .offset-lg-8 { - margin-right: 66.66666667%; - } - .offset-lg-9 { - margin-right: 75%; - } - .offset-lg-10 { - margin-right: 83.33333333%; - } - .offset-lg-11 { - margin-right: 91.66666667%; - } - .g-lg-0, - .gx-lg-0 { - --bs-gutter-x: 0; - } - .g-lg-0, - .gy-lg-0 { - --bs-gutter-y: 0; - } - .g-lg-1, - .gx-lg-1 { - --bs-gutter-x: 0.25rem; - } - .g-lg-1, - .gy-lg-1 { - --bs-gutter-y: 0.25rem; - } - .g-lg-2, - .gx-lg-2 { - --bs-gutter-x: 0.5rem; - } - .g-lg-2, - .gy-lg-2 { - --bs-gutter-y: 0.5rem; - } - .g-lg-3, - .gx-lg-3 { - --bs-gutter-x: 1rem; - } - .g-lg-3, - .gy-lg-3 { - --bs-gutter-y: 1rem; - } - .g-lg-4, - .gx-lg-4 { - --bs-gutter-x: 1.5rem; - } - .g-lg-4, - .gy-lg-4 { - --bs-gutter-y: 1.5rem; - } - .g-lg-5, - .gx-lg-5 { - --bs-gutter-x: 3rem; - } - .g-lg-5, - .gy-lg-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1200px) { - .col-xl { - flex: 1 0 0%; - } - .row-cols-xl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xl-0 { - margin-right: 0; - } - .offset-xl-1 { - margin-right: 8.33333333%; - } - .offset-xl-2 { - margin-right: 16.66666667%; - } - .offset-xl-3 { - margin-right: 25%; - } - .offset-xl-4 { - margin-right: 33.33333333%; - } - .offset-xl-5 { - margin-right: 41.66666667%; - } - .offset-xl-6 { - margin-right: 50%; - } - .offset-xl-7 { - margin-right: 58.33333333%; - } - .offset-xl-8 { - margin-right: 66.66666667%; - } - .offset-xl-9 { - margin-right: 75%; - } - .offset-xl-10 { - margin-right: 83.33333333%; - } - .offset-xl-11 { - margin-right: 91.66666667%; - } - .g-xl-0, - .gx-xl-0 { - --bs-gutter-x: 0; - } - .g-xl-0, - .gy-xl-0 { - --bs-gutter-y: 0; - } - .g-xl-1, - .gx-xl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xl-1, - .gy-xl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xl-2, - .gx-xl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xl-2, - .gy-xl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xl-3, - .gx-xl-3 { - --bs-gutter-x: 1rem; - } - .g-xl-3, - .gy-xl-3 { - --bs-gutter-y: 1rem; - } - .g-xl-4, - .gx-xl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xl-4, - .gy-xl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xl-5, - .gx-xl-5 { - --bs-gutter-x: 3rem; - } - .g-xl-5, - .gy-xl-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1400px) { - .col-xxl { - flex: 1 0 0%; - } - .row-cols-xxl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xxl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xxl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xxl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xxl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xxl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xxl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xxl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xxl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xxl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xxl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xxl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xxl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xxl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xxl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xxl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xxl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xxl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xxl-0 { - margin-right: 0; - } - .offset-xxl-1 { - margin-right: 8.33333333%; - } - .offset-xxl-2 { - margin-right: 16.66666667%; - } - .offset-xxl-3 { - margin-right: 25%; - } - .offset-xxl-4 { - margin-right: 33.33333333%; - } - .offset-xxl-5 { - margin-right: 41.66666667%; - } - .offset-xxl-6 { - margin-right: 50%; - } - .offset-xxl-7 { - margin-right: 58.33333333%; - } - .offset-xxl-8 { - margin-right: 66.66666667%; - } - .offset-xxl-9 { - margin-right: 75%; - } - .offset-xxl-10 { - margin-right: 83.33333333%; - } - .offset-xxl-11 { - margin-right: 91.66666667%; - } - .g-xxl-0, - .gx-xxl-0 { - --bs-gutter-x: 0; - } - .g-xxl-0, - .gy-xxl-0 { - --bs-gutter-y: 0; - } - .g-xxl-1, - .gx-xxl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xxl-1, - .gy-xxl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xxl-2, - .gx-xxl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xxl-2, - .gy-xxl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xxl-3, - .gx-xxl-3 { - --bs-gutter-x: 1rem; - } - .g-xxl-3, - .gy-xxl-3 { - --bs-gutter-y: 1rem; - } - .g-xxl-4, - .gx-xxl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xxl-4, - .gy-xxl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xxl-5, - .gx-xxl-5 { - --bs-gutter-x: 3rem; - } - .g-xxl-5, - .gy-xxl-5 { - --bs-gutter-y: 3rem; - } -} -.table { - --bs-table-color-type: initial; - --bs-table-bg-type: initial; - --bs-table-color-state: initial; - --bs-table-bg-state: initial; - --bs-table-color: var(--bs-emphasis-color); - --bs-table-bg: var(--bs-body-bg); - --bs-table-border-color: var(--bs-border-color); - --bs-table-accent-bg: transparent; - --bs-table-striped-color: var(--bs-emphasis-color); - --bs-table-striped-bg: rgba(var(--bs-emphasis-color-rgb), 0.05); - --bs-table-active-color: var(--bs-emphasis-color); - --bs-table-active-bg: rgba(var(--bs-emphasis-color-rgb), 0.1); - --bs-table-hover-color: var(--bs-emphasis-color); - --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); - width: 100%; - margin-bottom: 1rem; - vertical-align: top; - border-color: var(--bs-table-border-color); + margin-bottom: 1rem; + vertical-align: top; + border-color: var(--bs-table-border-color); } + .table > :not(caption) > * > * { - padding: 0.5rem 0.5rem; - color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color))); - background-color: var(--bs-table-bg); - border-bottom-width: var(--bs-border-width); - box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); + padding: 0.5rem 0.5rem; + color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color))); + background-color: var(--bs-table-bg); + border-bottom-width: var(--bs-border-width); + box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); } + .table > tbody { - vertical-align: inherit; + vertical-align: inherit; } + .table > thead { - vertical-align: bottom; + vertical-align: bottom; } .table-group-divider { - border-top: calc(var(--bs-border-width) * 2) solid currentcolor; + border-top: calc(var(--bs-border-width) * 2) solid currentcolor; } .caption-top { - caption-side: top; + caption-side: top; } .table-sm > :not(caption) > * > * { - padding: 0.25rem 0.25rem; + padding: 0.25rem 0.25rem; } .table-bordered > :not(caption) > * { - border-width: var(--bs-border-width) 0; + border-width: var(--bs-border-width) 0; } + .table-bordered > :not(caption) > * > * { - border-width: 0 var(--bs-border-width); + border-width: 0 var(--bs-border-width); } .table-borderless > :not(caption) > * > * { - border-bottom-width: 0; + border-bottom-width: 0; } + .table-borderless > :not(:first-child) { - border-top-width: 0; + border-top-width: 0; } .table-striped > tbody > tr:nth-of-type(odd) > * { - --bs-table-color-type: var(--bs-table-striped-color); - --bs-table-bg-type: var(--bs-table-striped-bg); + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); } .table-striped-columns > :not(caption) > tr > :nth-child(even) { - --bs-table-color-type: var(--bs-table-striped-color); - --bs-table-bg-type: var(--bs-table-striped-bg); + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); } .table-active { - --bs-table-color-state: var(--bs-table-active-color); - --bs-table-bg-state: var(--bs-table-active-bg); + --bs-table-color-state: var(--bs-table-active-color); + --bs-table-bg-state: var(--bs-table-active-bg); } .table-hover > tbody > tr:hover > * { - --bs-table-color-state: var(--bs-table-hover-color); - --bs-table-bg-state: var(--bs-table-hover-bg); + --bs-table-color-state: var(--bs-table-hover-color); + --bs-table-bg-state: var(--bs-table-hover-bg); } .table-primary { - --bs-table-color: #000; - --bs-table-bg: #cfe2ff; - --bs-table-border-color: #a6b5cc; - --bs-table-striped-bg: #c5d7f2; - --bs-table-striped-color: #000; - --bs-table-active-bg: #bacbe6; - --bs-table-active-color: #000; - --bs-table-hover-bg: #bfd1ec; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #cfe2ff; + --bs-table-border-color: #a6b5cc; + --bs-table-striped-bg: #c5d7f2; + --bs-table-striped-color: #000; + --bs-table-active-bg: #bacbe6; + --bs-table-active-color: #000; + --bs-table-hover-bg: #bfd1ec; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-secondary { - --bs-table-color: #000; - --bs-table-bg: #e2e3e5; - --bs-table-border-color: #b5b6b7; - --bs-table-striped-bg: #d7d8da; - --bs-table-striped-color: #000; - --bs-table-active-bg: #cbccce; - --bs-table-active-color: #000; - --bs-table-hover-bg: #d1d2d4; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #e2e3e5; + --bs-table-border-color: #b5b6b7; + --bs-table-striped-bg: #d7d8da; + --bs-table-striped-color: #000; + --bs-table-active-bg: #cbccce; + --bs-table-active-color: #000; + --bs-table-hover-bg: #d1d2d4; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-success { - --bs-table-color: #000; - --bs-table-bg: #d1e7dd; - --bs-table-border-color: #a7b9b1; - --bs-table-striped-bg: #c7dbd2; - --bs-table-striped-color: #000; - --bs-table-active-bg: #bcd0c7; - --bs-table-active-color: #000; - --bs-table-hover-bg: #c1d6cc; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #d1e7dd; + --bs-table-border-color: #a7b9b1; + --bs-table-striped-bg: #c7dbd2; + --bs-table-striped-color: #000; + --bs-table-active-bg: #bcd0c7; + --bs-table-active-color: #000; + --bs-table-hover-bg: #c1d6cc; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-info { - --bs-table-color: #000; - --bs-table-bg: #cff4fc; - --bs-table-border-color: #a6c3ca; - --bs-table-striped-bg: #c5e8ef; - --bs-table-striped-color: #000; - --bs-table-active-bg: #badce3; - --bs-table-active-color: #000; - --bs-table-hover-bg: #bfe2e9; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #cff4fc; + --bs-table-border-color: #a6c3ca; + --bs-table-striped-bg: #c5e8ef; + --bs-table-striped-color: #000; + --bs-table-active-bg: #badce3; + --bs-table-active-color: #000; + --bs-table-hover-bg: #bfe2e9; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-warning { - --bs-table-color: #000; - --bs-table-bg: #fff3cd; - --bs-table-border-color: #ccc2a4; - --bs-table-striped-bg: #f2e7c3; - --bs-table-striped-color: #000; - --bs-table-active-bg: #e6dbb9; - --bs-table-active-color: #000; - --bs-table-hover-bg: #ece1be; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #fff3cd; + --bs-table-border-color: #ccc2a4; + --bs-table-striped-bg: #f2e7c3; + --bs-table-striped-color: #000; + --bs-table-active-bg: #e6dbb9; + --bs-table-active-color: #000; + --bs-table-hover-bg: #ece1be; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-danger { - --bs-table-color: #000; - --bs-table-bg: #f8d7da; - --bs-table-border-color: #c6acae; - --bs-table-striped-bg: #eccccf; - --bs-table-striped-color: #000; - --bs-table-active-bg: #dfc2c4; - --bs-table-active-color: #000; - --bs-table-hover-bg: #e5c7ca; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #f8d7da; + --bs-table-border-color: #c6acae; + --bs-table-striped-bg: #eccccf; + --bs-table-striped-color: #000; + --bs-table-active-bg: #dfc2c4; + --bs-table-active-color: #000; + --bs-table-hover-bg: #e5c7ca; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-light { - --bs-table-color: #000; - --bs-table-bg: #f8f9fa; - --bs-table-border-color: #c6c7c8; - --bs-table-striped-bg: #ecedee; - --bs-table-striped-color: #000; - --bs-table-active-bg: #dfe0e1; - --bs-table-active-color: #000; - --bs-table-hover-bg: #e5e6e7; - --bs-table-hover-color: #000; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #000; + --bs-table-bg: #f8f9fa; + --bs-table-border-color: #c6c7c8; + --bs-table-striped-bg: #ecedee; + --bs-table-striped-color: #000; + --bs-table-active-bg: #dfe0e1; + --bs-table-active-color: #000; + --bs-table-hover-bg: #e5e6e7; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-dark { - --bs-table-color: #fff; - --bs-table-bg: #212529; - --bs-table-border-color: #4d5154; - --bs-table-striped-bg: #2c3034; - --bs-table-striped-color: #fff; - --bs-table-active-bg: #373b3e; - --bs-table-active-color: #fff; - --bs-table-hover-bg: #323539; - --bs-table-hover-color: #fff; - color: var(--bs-table-color); - border-color: var(--bs-table-border-color); + --bs-table-color: #fff; + --bs-table-bg: #212529; + --bs-table-border-color: #4d5154; + --bs-table-striped-bg: #2c3034; + --bs-table-striped-color: #fff; + --bs-table-active-bg: #373b3e; + --bs-table-active-color: #fff; + --bs-table-hover-bg: #323539; + --bs-table-hover-color: #fff; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); } .table-responsive { - overflow-x: auto; - -webkit-overflow-scrolling: touch; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } @media (max-width: 575.98px) { - .table-responsive-sm { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-sm { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 767.98px) { - .table-responsive-md { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-md { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 991.98px) { - .table-responsive-lg { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-lg { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 1199.98px) { - .table-responsive-xl { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-xl { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + @media (max-width: 1399.98px) { - .table-responsive-xxl { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } + .table-responsive-xxl { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } } + .form-label { - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } .col-form-label { - padding-top: calc(0.375rem + var(--bs-border-width)); - padding-bottom: calc(0.375rem + var(--bs-border-width)); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5; + padding-top: calc(0.375rem + var(--bs-border-width)); + padding-bottom: calc(0.375rem + var(--bs-border-width)); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; } .col-form-label-lg { - padding-top: calc(0.5rem + var(--bs-border-width)); - padding-bottom: calc(0.5rem + var(--bs-border-width)); - font-size: 1.25rem; + padding-top: calc(0.5rem + var(--bs-border-width)); + padding-bottom: calc(0.5rem + var(--bs-border-width)); + font-size: 1.25rem; } .col-form-label-sm { - padding-top: calc(0.25rem + var(--bs-border-width)); - padding-bottom: calc(0.25rem + var(--bs-border-width)); - font-size: 0.875rem; + padding-top: calc(0.25rem + var(--bs-border-width)); + padding-bottom: calc(0.25rem + var(--bs-border-width)); + font-size: 0.875rem; } .form-text { - margin-top: 0.25rem; - font-size: 0.875em; - color: var(--bs-secondary-color); + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-secondary-color); } .form-control { - display: block; - width: 100%; - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: var(--bs-body-color); - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: var(--bs-body-bg); - background-clip: padding-box; - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + display: block; + width: 100%; + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-body-bg); + background-clip: padding-box; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-control { - transition: none; - } + .form-control { + transition: none; + } } + .form-control[type=file] { - overflow: hidden; + overflow: hidden; } + .form-control[type=file]:not(:disabled):not([readonly]) { - cursor: pointer; + cursor: pointer; } + .form-control:focus { - color: var(--bs-body-color); - background-color: var(--bs-body-bg); - border-color: #86b7fe; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + color: var(--bs-body-color); + background-color: var(--bs-body-bg); + border-color: #86b7fe; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-control::-webkit-date-and-time-value { - min-width: 85px; - height: 1.5em; - margin: 0; + min-width: 85px; + height: 1.5em; + margin: 0; } + .form-control::-webkit-datetime-edit { - display: block; - padding: 0; + display: block; + padding: 0; } + .form-control::-moz-placeholder { - color: var(--bs-secondary-color); - opacity: 1; + color: var(--bs-secondary-color); + opacity: 1; } + .form-control::placeholder { - color: var(--bs-secondary-color); - opacity: 1; + color: var(--bs-secondary-color); + opacity: 1; } + .form-control:disabled { - background-color: var(--bs-secondary-bg); - opacity: 1; + background-color: var(--bs-secondary-bg); + opacity: 1; } + .form-control::-webkit-file-upload-button { - padding: 0.375rem 0.75rem; - margin: -0.375rem -0.75rem; - -webkit-margin-end: 0.75rem; - margin-inline-end: 0.75rem; - color: var(--bs-body-color); - background-color: var(--bs-tertiary-bg); - pointer-events: none; - border-color: inherit; - border-style: solid; - border-width: 0; - border-inline-end-width: var(--bs-border-width); - border-radius: 0; - -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + padding: 0.375rem 0.75rem; + margin: -0.375rem -0.75rem; + -webkit-margin-end: 0.75rem; + margin-inline-end: 0.75rem; + color: var(--bs-body-color); + background-color: var(--bs-tertiary-bg); + pointer-events: none; + border-color: inherit; + border-style: solid; + border-width: 0; + border-inline-end-width: var(--bs-border-width); + border-radius: 0; + -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + .form-control::file-selector-button { - padding: 0.375rem 0.75rem; - margin: -0.375rem -0.75rem; - -webkit-margin-end: 0.75rem; - margin-inline-end: 0.75rem; - color: var(--bs-body-color); - background-color: var(--bs-tertiary-bg); - pointer-events: none; - border-color: inherit; - border-style: solid; - border-width: 0; - border-inline-end-width: var(--bs-border-width); - border-radius: 0; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + padding: 0.375rem 0.75rem; + margin: -0.375rem -0.75rem; + -webkit-margin-end: 0.75rem; + margin-inline-end: 0.75rem; + color: var(--bs-body-color); + background-color: var(--bs-tertiary-bg); + pointer-events: none; + border-color: inherit; + border-style: solid; + border-width: 0; + border-inline-end-width: var(--bs-border-width); + border-radius: 0; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-control::-webkit-file-upload-button { - -webkit-transition: none; - transition: none; - } - .form-control::file-selector-button { - transition: none; - } + .form-control::-webkit-file-upload-button { + -webkit-transition: none; + transition: none; + } + + .form-control::file-selector-button { + transition: none; + } } + .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } + .form-control:hover:not(:disabled):not([readonly])::file-selector-button { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } .form-control-plaintext { - display: block; - width: 100%; - padding: 0.375rem 0; - margin-bottom: 0; - line-height: 1.5; - color: var(--bs-body-color); - background-color: transparent; - border: solid transparent; - border-width: var(--bs-border-width) 0; + display: block; + width: 100%; + padding: 0.375rem 0; + margin-bottom: 0; + line-height: 1.5; + color: var(--bs-body-color); + background-color: transparent; + border: solid transparent; + border-width: var(--bs-border-width) 0; } + .form-control-plaintext:focus { - outline: 0; + outline: 0; } + .form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { - padding-left: 0; - padding-right: 0; + padding-left: 0; + padding-right: 0; } .form-control-sm { - min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - border-radius: var(--bs-border-radius-sm); + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); } + .form-control-sm::-webkit-file-upload-button { - padding: 0.25rem 0.5rem; - margin: -0.25rem -0.5rem; - -webkit-margin-end: 0.5rem; - margin-inline-end: 0.5rem; + padding: 0.25rem 0.5rem; + margin: -0.25rem -0.5rem; + -webkit-margin-end: 0.5rem; + margin-inline-end: 0.5rem; } + .form-control-sm::file-selector-button { - padding: 0.25rem 0.5rem; - margin: -0.25rem -0.5rem; - -webkit-margin-end: 0.5rem; - margin-inline-end: 0.5rem; + padding: 0.25rem 0.5rem; + margin: -0.25rem -0.5rem; + -webkit-margin-end: 0.5rem; + margin-inline-end: 0.5rem; } .form-control-lg { - min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); - padding: 0.5rem 1rem; - font-size: 1.25rem; - border-radius: var(--bs-border-radius-lg); + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); } + .form-control-lg::-webkit-file-upload-button { - padding: 0.5rem 1rem; - margin: -0.5rem -1rem; - -webkit-margin-end: 1rem; - margin-inline-end: 1rem; + padding: 0.5rem 1rem; + margin: -0.5rem -1rem; + -webkit-margin-end: 1rem; + margin-inline-end: 1rem; } + .form-control-lg::file-selector-button { - padding: 0.5rem 1rem; - margin: -0.5rem -1rem; - -webkit-margin-end: 1rem; - margin-inline-end: 1rem; + padding: 0.5rem 1rem; + margin: -0.5rem -1rem; + -webkit-margin-end: 1rem; + margin-inline-end: 1rem; } textarea.form-control { - min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); + min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); } + textarea.form-control-sm { - min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } + textarea.form-control-lg { - min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-control-color { - width: 3rem; - height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); - padding: 0.375rem; + width: 3rem; + height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); + padding: 0.375rem; } + .form-control-color:not(:disabled):not([readonly]) { - cursor: pointer; + cursor: pointer; } + .form-control-color::-moz-color-swatch { - border: 0 !important; - border-radius: var(--bs-border-radius); + border: 0 !important; + border-radius: var(--bs-border-radius); } + .form-control-color::-webkit-color-swatch { - border: 0 !important; - border-radius: var(--bs-border-radius); + border: 0 !important; + border-radius: var(--bs-border-radius); } + .form-control-color.form-control-sm { - height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); + height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } + .form-control-color.form-control-lg { - height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); + height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-select { - --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); - display: block; - width: 100%; - padding: 0.375rem 0.75rem 0.375rem 2.25rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: var(--bs-body-color); - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: var(--bs-body-bg); - background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none); - background-repeat: no-repeat; - background-position: left 0.75rem center; - background-size: 16px 12px; - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); + display: block; + width: 100%; + padding: 0.375rem 0.75rem 0.375rem 2.25rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-body-bg); + background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none); + background-repeat: no-repeat; + background-position: left 0.75rem center; + background-size: 16px 12px; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-select { - transition: none; - } + .form-select { + transition: none; + } } + .form-select:focus { - border-color: #86b7fe; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + border-color: #86b7fe; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-select[multiple], .form-select[size]:not([size="1"]) { - padding-left: 0.75rem; - background-image: none; + padding-left: 0.75rem; + background-image: none; } + .form-select:disabled { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } + .form-select:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 var(--bs-body-color); + color: transparent; + text-shadow: 0 0 0 var(--bs-body-color); } .form-select-sm { - padding-top: 0.25rem; - padding-bottom: 0.25rem; - padding-right: 0.5rem; - font-size: 0.875rem; - border-radius: var(--bs-border-radius-sm); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-right: 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); } .form-select-lg { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-right: 1rem; - font-size: 1.25rem; - border-radius: var(--bs-border-radius-lg); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-right: 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); } [data-bs-theme=dark] .form-select { - --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); } .form-check { - display: block; - min-height: 1.5rem; - padding-right: 1.5em; - margin-bottom: 0.125rem; + display: block; + min-height: 1.5rem; + padding-right: 1.5em; + margin-bottom: 0.125rem; } + .form-check .form-check-input { - float: right; - margin-right: -1.5em; + float: right; + margin-right: -1.5em; } .form-check-reverse { - padding-left: 1.5em; - padding-right: 0; - text-align: left; + padding-left: 1.5em; + padding-right: 0; + text-align: left; } + .form-check-reverse .form-check-input { - float: left; - margin-left: -1.5em; - margin-right: 0; + float: left; + margin-left: -1.5em; + margin-right: 0; } .form-check-input { - --bs-form-check-bg: var(--bs-body-bg); - flex-shrink: 0; - width: 1em; - height: 1em; - margin-top: 0.25em; - vertical-align: top; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: var(--bs-form-check-bg); - background-image: var(--bs-form-check-bg-image); - background-repeat: no-repeat; - background-position: center; - background-size: contain; - border: var(--bs-border-width) solid var(--bs-border-color); - -webkit-print-color-adjust: exact; - color-adjust: exact; - print-color-adjust: exact; + --bs-form-check-bg: var(--bs-body-bg); + flex-shrink: 0; + width: 1em; + height: 1em; + margin-top: 0.25em; + vertical-align: top; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-form-check-bg); + background-image: var(--bs-form-check-bg-image); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + border: var(--bs-border-width) solid var(--bs-border-color); + -webkit-print-color-adjust: exact; + color-adjust: exact; + print-color-adjust: exact; } + .form-check-input[type=checkbox] { - border-radius: 0.25em; + border-radius: 0.25em; } + .form-check-input[type=radio] { - border-radius: 50%; + border-radius: 50%; } + .form-check-input:active { - filter: brightness(90%); + filter: brightness(90%); } + .form-check-input:focus { - border-color: #86b7fe; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + border-color: #86b7fe; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-check-input:checked { - background-color: #0d6efd; - border-color: #0d6efd; + background-color: #0d6efd; + border-color: #0d6efd; } + .form-check-input:checked[type=checkbox] { - --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); } + .form-check-input:checked[type=radio] { - --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } + .form-check-input[type=checkbox]:indeterminate { - background-color: #0d6efd; - border-color: #0d6efd; - --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); + background-color: #0d6efd; + border-color: #0d6efd; + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); } + .form-check-input:disabled { - pointer-events: none; - filter: none; - opacity: 0.5; + pointer-events: none; + filter: none; + opacity: 0.5; } + .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { - cursor: default; - opacity: 0.5; + cursor: default; + opacity: 0.5; } .form-switch { - padding-right: 2.5em; + padding-right: 2.5em; } + .form-switch .form-check-input { - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); - width: 2em; - margin-right: -2.5em; - background-image: var(--bs-form-switch-bg); - background-position: right center; - border-radius: 2em; - transition: background-position 0.15s ease-in-out; + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); + width: 2em; + margin-right: -2.5em; + background-image: var(--bs-form-switch-bg); + background-position: right center; + border-radius: 2em; + transition: background-position 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-switch .form-check-input { - transition: none; - } + .form-switch .form-check-input { + transition: none; + } } + .form-switch .form-check-input:focus { - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); } + .form-switch .form-check-input:checked { - background-position: left center; - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); + background-position: left center; + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } + .form-switch.form-check-reverse { - padding-left: 2.5em; - padding-right: 0; + padding-left: 2.5em; + padding-right: 0; } + .form-switch.form-check-reverse .form-check-input { - margin-left: -2.5em; - margin-right: 0; + margin-left: -2.5em; + margin-right: 0; } .form-check-inline { - display: inline-block; - margin-left: 1rem; + display: inline-block; + margin-left: 1rem; } .btn-check { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; } + .btn-check[disabled] + .btn, .btn-check:disabled + .btn { - pointer-events: none; - filter: none; - opacity: 0.65; + pointer-events: none; + filter: none; + opacity: 0.65; } [data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus) { - --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e"); + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e"); } .form-range { - width: 100%; - height: 1.5rem; - padding: 0; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: transparent; + width: 100%; + height: 1.5rem; + padding: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; } + .form-range:focus { - outline: 0; + outline: 0; } + .form-range:focus::-webkit-slider-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-range:focus::-moz-range-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .form-range::-moz-focus-outer { - border: 0; + border: 0; } + .form-range::-webkit-slider-thumb { - width: 1rem; - height: 1rem; - margin-top: -0.25rem; - -webkit-appearance: none; - appearance: none; - background-color: #0d6efd; - border: 0; - border-radius: 1rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + -webkit-appearance: none; + appearance: none; + background-color: #0d6efd; + border: 0; + border-radius: 1rem; + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-range::-webkit-slider-thumb { - -webkit-transition: none; - transition: none; - } + .form-range::-webkit-slider-thumb { + -webkit-transition: none; + transition: none; + } } + .form-range::-webkit-slider-thumb:active { - background-color: #b6d4fe; + background-color: #b6d4fe; } + .form-range::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: var(--bs-secondary-bg); - border-color: transparent; - border-radius: 1rem; + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: var(--bs-secondary-bg); + border-color: transparent; + border-radius: 1rem; } + .form-range::-moz-range-thumb { - width: 1rem; - height: 1rem; - -moz-appearance: none; - appearance: none; - background-color: #0d6efd; - border: 0; - border-radius: 1rem; - -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + width: 1rem; + height: 1rem; + -moz-appearance: none; + appearance: none; + background-color: #0d6efd; + border: 0; + border-radius: 1rem; + -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-range::-moz-range-thumb { - -moz-transition: none; - transition: none; - } + .form-range::-moz-range-thumb { + -moz-transition: none; + transition: none; + } } + .form-range::-moz-range-thumb:active { - background-color: #b6d4fe; + background-color: #b6d4fe; } + .form-range::-moz-range-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: var(--bs-secondary-bg); - border-color: transparent; - border-radius: 1rem; + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: var(--bs-secondary-bg); + border-color: transparent; + border-radius: 1rem; } + .form-range:disabled { - pointer-events: none; + pointer-events: none; } + .form-range:disabled::-webkit-slider-thumb { - background-color: var(--bs-secondary-color); + background-color: var(--bs-secondary-color); } + .form-range:disabled::-moz-range-thumb { - background-color: var(--bs-secondary-color); + background-color: var(--bs-secondary-color); } .form-floating { - position: relative; + position: relative; } + .form-floating > .form-control, .form-floating > .form-control-plaintext, .form-floating > .form-select { - height: calc(3.5rem + calc(var(--bs-border-width) * 2)); - min-height: calc(3.5rem + calc(var(--bs-border-width) * 2)); - line-height: 1.25; + height: calc(3.5rem + calc(var(--bs-border-width) * 2)); + min-height: calc(3.5rem + calc(var(--bs-border-width) * 2)); + line-height: 1.25; } + .form-floating > label { - position: absolute; - top: 0; - right: 0; - z-index: 2; - height: 100%; - padding: 1rem 0.75rem; - overflow: hidden; - text-align: start; - text-overflow: ellipsis; - white-space: nowrap; - pointer-events: none; - border: var(--bs-border-width) solid transparent; - transform-origin: 100% 0; - transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; + position: absolute; + top: 0; + right: 0; + z-index: 2; + height: 100%; + padding: 1rem 0.75rem; + overflow: hidden; + text-align: start; + text-overflow: ellipsis; + white-space: nowrap; + pointer-events: none; + border: var(--bs-border-width) solid transparent; + transform-origin: 100% 0; + transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .form-floating > label { - transition: none; - } + .form-floating > label { + transition: none; + } } + .form-floating > .form-control, .form-floating > .form-control-plaintext { - padding: 1rem 0.75rem; + padding: 1rem 0.75rem; } + .form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder { - color: transparent; + color: transparent; } + .form-floating > .form-control::placeholder, .form-floating > .form-control-plaintext::placeholder { - color: transparent; + color: transparent; } + .form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown), .form-floating > .form-control-plaintext:focus, .form-floating > .form-control-plaintext:not(:placeholder-shown) { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-control:-webkit-autofill, .form-floating > .form-control-plaintext:-webkit-autofill { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-select { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + padding-top: 1.625rem; + padding-bottom: 0.625rem; } + .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); - transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); } + .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-control-plaintext ~ label, .form-floating > .form-select ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); - transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); } + .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label::after { - position: absolute; - inset: 1rem 0.375rem; - z-index: -1; - height: 1.5em; - content: ""; - background-color: var(--bs-body-bg); - border-radius: var(--bs-border-radius); + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; + height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); } + .form-floating > .form-control:focus ~ label::after, .form-floating > .form-control:not(:placeholder-shown) ~ label::after, .form-floating > .form-control-plaintext ~ label::after, .form-floating > .form-select ~ label::after { - position: absolute; - inset: 1rem 0.375rem; - z-index: -1; - height: 1.5em; - content: ""; - background-color: var(--bs-body-bg); - border-radius: var(--bs-border-radius); + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; + height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); } + .form-floating > .form-control:-webkit-autofill ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); - transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(-0.15rem); } + .form-floating > .form-control-plaintext ~ label { - border-width: var(--bs-border-width) 0; + border-width: var(--bs-border-width) 0; } + .form-floating > :disabled ~ label, .form-floating > .form-control:disabled ~ label { - color: #6c757d; + color: #6c757d; } + .form-floating > :disabled ~ label::after, .form-floating > .form-control:disabled ~ label::after { - background-color: var(--bs-secondary-bg); + background-color: var(--bs-secondary-bg); } .input-group { - position: relative; - display: flex; - flex-wrap: wrap; - align-items: stretch; - width: 100%; + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100%; } + .input-group > .form-control, .input-group > .form-select, .input-group > .form-floating { - position: relative; - flex: 1 1 auto; - width: 1%; - min-width: 0; + position: relative; + flex: 1 1 auto; + width: 1%; + min-width: 0; } + .input-group > .form-control:focus, .input-group > .form-select:focus, .input-group > .form-floating:focus-within { - z-index: 5; + z-index: 5; } + .input-group .btn { - position: relative; - z-index: 2; + position: relative; + z-index: 2; } + .input-group .btn:focus { - z-index: 5; + z-index: 5; } .input-group-text { - display: flex; - align-items: center; - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: var(--bs-body-color); - text-align: center; - white-space: nowrap; - background-color: var(--bs-tertiary-bg); - border: var(--bs-border-width) solid var(--bs-border-color); - border-radius: var(--bs-border-radius); + display: flex; + align-items: center; + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + text-align: center; + white-space: nowrap; + background-color: var(--bs-tertiary-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); } .input-group-lg > .form-control, .input-group-lg > .form-select, .input-group-lg > .input-group-text, .input-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - border-radius: var(--bs-border-radius-lg); + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); } .input-group-sm > .form-control, .input-group-sm > .form-select, .input-group-sm > .input-group-text, .input-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - border-radius: var(--bs-border-radius-sm); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); } .input-group-lg > .form-select, .input-group-sm > .form-select { - padding-left: 3rem; + padding-left: 3rem; } .input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), .input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3), .input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control, .input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select { - border-top-left-radius: 0; - border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), .input-group.has-validation > .dropdown-toggle:nth-last-child(n+4), .input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-control, .input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-select { - border-top-left-radius: 0; - border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { - margin-right: calc(var(--bs-border-width) * -1); - border-top-right-radius: 0; - border-bottom-right-radius: 0; + margin-right: calc(var(--bs-border-width) * -1); + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .input-group > .form-floating:not(:first-child) > .form-control, .input-group > .form-floating:not(:first-child) > .form-select { - border-top-right-radius: 0; - border-bottom-right-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; } .valid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 0.875em; - color: var(--bs-form-valid-color); + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-form-valid-color); } .valid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: 0.1rem; - font-size: 0.875rem; - color: #fff; - background-color: var(--bs-success); - border-radius: var(--bs-border-radius); + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + color: #fff; + background-color: var(--bs-success); + border-radius: var(--bs-border-radius); } .was-validated :valid ~ .valid-feedback, .was-validated :valid ~ .valid-tooltip, .is-valid ~ .valid-feedback, .is-valid ~ .valid-tooltip { - display: block; + display: block; } .was-validated .form-control:valid, .form-control.is-valid { - border-color: var(--bs-form-valid-border-color); - padding-left: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: left calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + border-color: var(--bs-form-valid-border-color); + padding-left: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: left calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: var(--bs-form-valid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { - padding-left: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem); + padding-left: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem); } .was-validated .form-select:valid, .form-select.is-valid { - border-color: var(--bs-form-valid-border-color); + border-color: var(--bs-form-valid-border-color); } + .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { - --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - padding-left: 4.125rem; - background-position: left 0.75rem center, center left 2.25rem; - background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + padding-left: 4.125rem; + background-position: left 0.75rem center, center left 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-select:valid:focus, .form-select.is-valid:focus { - border-color: var(--bs-form-valid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated .form-control-color:valid, .form-control-color.is-valid { - width: calc(3rem + calc(1.5em + 0.75rem)); + width: calc(3rem + calc(1.5em + 0.75rem)); } .was-validated .form-check-input:valid, .form-check-input.is-valid { - border-color: var(--bs-form-valid-border-color); + border-color: var(--bs-form-valid-border-color); } + .was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { - background-color: var(--bs-form-valid-color); + background-color: var(--bs-form-valid-color); } + .was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { - box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } + .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: var(--bs-form-valid-color); + color: var(--bs-form-valid-color); } .form-check-inline .form-check-input ~ .valid-feedback { - margin-right: 0.5em; + margin-right: 0.5em; } .was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid, @@ -2877,89 +3238,95 @@ textarea.form-control-lg { .input-group > .form-select:not(:focus).is-valid, .was-validated .input-group > .form-floating:not(:focus-within):valid, .input-group > .form-floating:not(:focus-within).is-valid { - z-index: 3; + z-index: 3; } .invalid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 0.875em; - color: var(--bs-form-invalid-color); + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-form-invalid-color); } .invalid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: 0.1rem; - font-size: 0.875rem; - color: #fff; - background-color: var(--bs-danger); - border-radius: var(--bs-border-radius); + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + color: #fff; + background-color: var(--bs-danger); + border-radius: var(--bs-border-radius); } .was-validated :invalid ~ .invalid-feedback, .was-validated :invalid ~ .invalid-tooltip, .is-invalid ~ .invalid-feedback, .is-invalid ~ .invalid-tooltip { - display: block; + display: block; } .was-validated .form-control:invalid, .form-control.is-invalid { - border-color: var(--bs-form-invalid-border-color); - padding-left: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: left calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + border-color: var(--bs-form-invalid-border-color); + padding-left: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: left calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: var(--bs-form-invalid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { - padding-left: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem); + padding-left: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) left calc(0.375em + 0.1875rem); } .was-validated .form-select:invalid, .form-select.is-invalid { - border-color: var(--bs-form-invalid-border-color); + border-color: var(--bs-form-invalid-border-color); } + .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { - --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); - padding-left: 4.125rem; - background-position: left 0.75rem center, center left 2.25rem; - background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + padding-left: 4.125rem; + background-position: left 0.75rem center, center left 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { - border-color: var(--bs-form-invalid-border-color); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated .form-control-color:invalid, .form-control-color.is-invalid { - width: calc(3rem + calc(1.5em + 0.75rem)); + width: calc(3rem + calc(1.5em + 0.75rem)); } .was-validated .form-check-input:invalid, .form-check-input.is-invalid { - border-color: var(--bs-form-invalid-border-color); + border-color: var(--bs-form-invalid-border-color); } + .was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { - background-color: var(--bs-form-invalid-color); + background-color: var(--bs-form-invalid-color); } + .was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { - box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } + .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: var(--bs-form-invalid-color); + color: var(--bs-form-invalid-color); } .form-check-inline .form-check-input ~ .invalid-feedback { - margin-right: 0.5em; + margin-right: 0.5em; } .was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid, @@ -2967,433 +3334,449 @@ textarea.form-control-lg { .input-group > .form-select:not(:focus).is-invalid, .was-validated .input-group > .form-floating:not(:focus-within):invalid, .input-group > .form-floating:not(:focus-within).is-invalid { - z-index: 4; + z-index: 4; } .btn { - --bs-btn-padding-x: 0.75rem; - --bs-btn-padding-y: 0.375rem; - --bs-btn-font-family: ; - --bs-btn-font-size: 1rem; - --bs-btn-font-weight: 400; - --bs-btn-line-height: 1.5; - --bs-btn-color: var(--bs-body-color); - --bs-btn-bg: transparent; - --bs-btn-border-width: var(--bs-border-width); - --bs-btn-border-color: transparent; - --bs-btn-border-radius: var(--bs-border-radius); - --bs-btn-hover-border-color: transparent; - --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); - --bs-btn-disabled-opacity: 0.65; - --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5); - display: inline-block; - padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x); - font-family: var(--bs-btn-font-family); - font-size: var(--bs-btn-font-size); - font-weight: var(--bs-btn-font-weight); - line-height: var(--bs-btn-line-height); - color: var(--bs-btn-color); - text-align: center; - text-decoration: none; - vertical-align: middle; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); - border-radius: var(--bs-btn-border-radius); - background-color: var(--bs-btn-bg); - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + --bs-btn-padding-x: 0.75rem; + --bs-btn-padding-y: 0.375rem; + --bs-btn-font-family: ; + --bs-btn-font-size: 1rem; + --bs-btn-font-weight: 400; + --bs-btn-line-height: 1.5; + --bs-btn-color: var(--bs-body-color); + --bs-btn-bg: transparent; + --bs-btn-border-width: var(--bs-border-width); + --bs-btn-border-color: transparent; + --bs-btn-border-radius: var(--bs-border-radius); + --bs-btn-hover-border-color: transparent; + --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); + --bs-btn-disabled-opacity: 0.65; + --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5); + display: inline-block; + padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x); + font-family: var(--bs-btn-font-family); + font-size: var(--bs-btn-font-size); + font-weight: var(--bs-btn-font-weight); + line-height: var(--bs-btn-line-height); + color: var(--bs-btn-color); + text-align: center; + text-decoration: none; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); + border-radius: var(--bs-btn-border-radius); + background-color: var(--bs-btn-bg); + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .btn { - transition: none; - } + .btn { + transition: none; + } } + .btn:hover { - color: var(--bs-btn-hover-color); - background-color: var(--bs-btn-hover-bg); - border-color: var(--bs-btn-hover-border-color); + color: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); } + .btn-check + .btn:hover { - color: var(--bs-btn-color); - background-color: var(--bs-btn-bg); - border-color: var(--bs-btn-border-color); + color: var(--bs-btn-color); + background-color: var(--bs-btn-bg); + border-color: var(--bs-btn-border-color); } + .btn:focus-visible { - color: var(--bs-btn-hover-color); - background-color: var(--bs-btn-hover-bg); - border-color: var(--bs-btn-hover-border-color); - outline: 0; - box-shadow: var(--bs-btn-focus-box-shadow); + color: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); + outline: 0; + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn-check:focus-visible + .btn { - border-color: var(--bs-btn-hover-border-color); - outline: 0; - box-shadow: var(--bs-btn-focus-box-shadow); + border-color: var(--bs-btn-hover-border-color); + outline: 0; + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show { - color: var(--bs-btn-active-color); - background-color: var(--bs-btn-active-bg); - border-color: var(--bs-btn-active-border-color); + color: var(--bs-btn-active-color); + background-color: var(--bs-btn-active-bg); + border-color: var(--bs-btn-active-border-color); } + .btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible { - box-shadow: var(--bs-btn-focus-box-shadow); + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn-check:checked:focus-visible + .btn { - box-shadow: var(--bs-btn-focus-box-shadow); + box-shadow: var(--bs-btn-focus-box-shadow); } + .btn:disabled, .btn.disabled, fieldset:disabled .btn { - color: var(--bs-btn-disabled-color); - pointer-events: none; - background-color: var(--bs-btn-disabled-bg); - border-color: var(--bs-btn-disabled-border-color); - opacity: var(--bs-btn-disabled-opacity); + color: var(--bs-btn-disabled-color); + pointer-events: none; + background-color: var(--bs-btn-disabled-bg); + border-color: var(--bs-btn-disabled-border-color); + opacity: var(--bs-btn-disabled-opacity); } .btn-primary { - --bs-btn-color: #fff; - --bs-btn-bg: #0d6efd; - --bs-btn-border-color: #0d6efd; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #0b5ed7; - --bs-btn-hover-border-color: #0a58ca; - --bs-btn-focus-shadow-rgb: 49, 132, 253; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #0a58ca; - --bs-btn-active-border-color: #0a53be; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #0d6efd; - --bs-btn-disabled-border-color: #0d6efd; + --bs-btn-color: #fff; + --bs-btn-bg: #0d6efd; + --bs-btn-border-color: #0d6efd; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #0b5ed7; + --bs-btn-hover-border-color: #0a58ca; + --bs-btn-focus-shadow-rgb: 49, 132, 253; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #0a58ca; + --bs-btn-active-border-color: #0a53be; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #0d6efd; + --bs-btn-disabled-border-color: #0d6efd; } .btn-secondary { - --bs-btn-color: #fff; - --bs-btn-bg: #6c757d; - --bs-btn-border-color: #6c757d; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #5c636a; - --bs-btn-hover-border-color: #565e64; - --bs-btn-focus-shadow-rgb: 130, 138, 145; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #565e64; - --bs-btn-active-border-color: #51585e; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #6c757d; - --bs-btn-disabled-border-color: #6c757d; + --bs-btn-color: #fff; + --bs-btn-bg: #6c757d; + --bs-btn-border-color: #6c757d; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #5c636a; + --bs-btn-hover-border-color: #565e64; + --bs-btn-focus-shadow-rgb: 130, 138, 145; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #565e64; + --bs-btn-active-border-color: #51585e; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #6c757d; + --bs-btn-disabled-border-color: #6c757d; } .btn-success { - --bs-btn-color: #fff; - --bs-btn-bg: #198754; - --bs-btn-border-color: #198754; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #157347; - --bs-btn-hover-border-color: #146c43; - --bs-btn-focus-shadow-rgb: 60, 153, 110; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #146c43; - --bs-btn-active-border-color: #13653f; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #198754; - --bs-btn-disabled-border-color: #198754; + --bs-btn-color: #fff; + --bs-btn-bg: #198754; + --bs-btn-border-color: #198754; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #157347; + --bs-btn-hover-border-color: #146c43; + --bs-btn-focus-shadow-rgb: 60, 153, 110; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #146c43; + --bs-btn-active-border-color: #13653f; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #198754; + --bs-btn-disabled-border-color: #198754; } .btn-info { - --bs-btn-color: #000; - --bs-btn-bg: #0dcaf0; - --bs-btn-border-color: #0dcaf0; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #31d2f2; - --bs-btn-hover-border-color: #25cff2; - --bs-btn-focus-shadow-rgb: 11, 172, 204; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #3dd5f3; - --bs-btn-active-border-color: #25cff2; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #000; - --bs-btn-disabled-bg: #0dcaf0; - --bs-btn-disabled-border-color: #0dcaf0; + --bs-btn-color: #000; + --bs-btn-bg: #0dcaf0; + --bs-btn-border-color: #0dcaf0; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #31d2f2; + --bs-btn-hover-border-color: #25cff2; + --bs-btn-focus-shadow-rgb: 11, 172, 204; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #3dd5f3; + --bs-btn-active-border-color: #25cff2; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #0dcaf0; + --bs-btn-disabled-border-color: #0dcaf0; } .btn-warning { - --bs-btn-color: #000; - --bs-btn-bg: #ffc107; - --bs-btn-border-color: #ffc107; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #ffca2c; - --bs-btn-hover-border-color: #ffc720; - --bs-btn-focus-shadow-rgb: 217, 164, 6; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #ffcd39; - --bs-btn-active-border-color: #ffc720; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #000; - --bs-btn-disabled-bg: #ffc107; - --bs-btn-disabled-border-color: #ffc107; + --bs-btn-color: #000; + --bs-btn-bg: #ffc107; + --bs-btn-border-color: #ffc107; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #ffca2c; + --bs-btn-hover-border-color: #ffc720; + --bs-btn-focus-shadow-rgb: 217, 164, 6; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #ffcd39; + --bs-btn-active-border-color: #ffc720; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #ffc107; + --bs-btn-disabled-border-color: #ffc107; } .btn-danger { - --bs-btn-color: #fff; - --bs-btn-bg: #dc3545; - --bs-btn-border-color: #dc3545; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #bb2d3b; - --bs-btn-hover-border-color: #b02a37; - --bs-btn-focus-shadow-rgb: 225, 83, 97; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #b02a37; - --bs-btn-active-border-color: #a52834; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #dc3545; - --bs-btn-disabled-border-color: #dc3545; + --bs-btn-color: #fff; + --bs-btn-bg: #dc3545; + --bs-btn-border-color: #dc3545; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #bb2d3b; + --bs-btn-hover-border-color: #b02a37; + --bs-btn-focus-shadow-rgb: 225, 83, 97; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #b02a37; + --bs-btn-active-border-color: #a52834; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #dc3545; + --bs-btn-disabled-border-color: #dc3545; } .btn-light { - --bs-btn-color: #000; - --bs-btn-bg: #f8f9fa; - --bs-btn-border-color: #f8f9fa; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #d3d4d5; - --bs-btn-hover-border-color: #c6c7c8; - --bs-btn-focus-shadow-rgb: 211, 212, 213; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #c6c7c8; - --bs-btn-active-border-color: #babbbc; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #000; - --bs-btn-disabled-bg: #f8f9fa; - --bs-btn-disabled-border-color: #f8f9fa; + --bs-btn-color: #000; + --bs-btn-bg: #f8f9fa; + --bs-btn-border-color: #f8f9fa; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #d3d4d5; + --bs-btn-hover-border-color: #c6c7c8; + --bs-btn-focus-shadow-rgb: 211, 212, 213; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #c6c7c8; + --bs-btn-active-border-color: #babbbc; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #f8f9fa; + --bs-btn-disabled-border-color: #f8f9fa; } .btn-dark { - --bs-btn-color: #fff; - --bs-btn-bg: #212529; - --bs-btn-border-color: #212529; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #424649; - --bs-btn-hover-border-color: #373b3e; - --bs-btn-focus-shadow-rgb: 66, 70, 73; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #4d5154; - --bs-btn-active-border-color: #373b3e; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #fff; - --bs-btn-disabled-bg: #212529; - --bs-btn-disabled-border-color: #212529; + --bs-btn-color: #fff; + --bs-btn-bg: #212529; + --bs-btn-border-color: #212529; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #424649; + --bs-btn-hover-border-color: #373b3e; + --bs-btn-focus-shadow-rgb: 66, 70, 73; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #4d5154; + --bs-btn-active-border-color: #373b3e; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #212529; + --bs-btn-disabled-border-color: #212529; } .btn-outline-primary { - --bs-btn-color: #0d6efd; - --bs-btn-border-color: #0d6efd; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #0d6efd; - --bs-btn-hover-border-color: #0d6efd; - --bs-btn-focus-shadow-rgb: 13, 110, 253; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #0d6efd; - --bs-btn-active-border-color: #0d6efd; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #0d6efd; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #0d6efd; - --bs-gradient: none; + --bs-btn-color: #0d6efd; + --bs-btn-border-color: #0d6efd; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #0d6efd; + --bs-btn-hover-border-color: #0d6efd; + --bs-btn-focus-shadow-rgb: 13, 110, 253; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #0d6efd; + --bs-btn-active-border-color: #0d6efd; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #0d6efd; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #0d6efd; + --bs-gradient: none; } .btn-outline-secondary { - --bs-btn-color: #6c757d; - --bs-btn-border-color: #6c757d; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #6c757d; - --bs-btn-hover-border-color: #6c757d; - --bs-btn-focus-shadow-rgb: 108, 117, 125; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #6c757d; - --bs-btn-active-border-color: #6c757d; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #6c757d; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #6c757d; - --bs-gradient: none; + --bs-btn-color: #6c757d; + --bs-btn-border-color: #6c757d; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #6c757d; + --bs-btn-hover-border-color: #6c757d; + --bs-btn-focus-shadow-rgb: 108, 117, 125; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #6c757d; + --bs-btn-active-border-color: #6c757d; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #6c757d; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #6c757d; + --bs-gradient: none; } .btn-outline-success { - --bs-btn-color: #198754; - --bs-btn-border-color: #198754; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #198754; - --bs-btn-hover-border-color: #198754; - --bs-btn-focus-shadow-rgb: 25, 135, 84; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #198754; - --bs-btn-active-border-color: #198754; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #198754; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #198754; - --bs-gradient: none; + --bs-btn-color: #198754; + --bs-btn-border-color: #198754; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #198754; + --bs-btn-hover-border-color: #198754; + --bs-btn-focus-shadow-rgb: 25, 135, 84; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #198754; + --bs-btn-active-border-color: #198754; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #198754; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #198754; + --bs-gradient: none; } .btn-outline-info { - --bs-btn-color: #0dcaf0; - --bs-btn-border-color: #0dcaf0; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #0dcaf0; - --bs-btn-hover-border-color: #0dcaf0; - --bs-btn-focus-shadow-rgb: 13, 202, 240; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #0dcaf0; - --bs-btn-active-border-color: #0dcaf0; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #0dcaf0; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #0dcaf0; - --bs-gradient: none; + --bs-btn-color: #0dcaf0; + --bs-btn-border-color: #0dcaf0; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #0dcaf0; + --bs-btn-hover-border-color: #0dcaf0; + --bs-btn-focus-shadow-rgb: 13, 202, 240; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #0dcaf0; + --bs-btn-active-border-color: #0dcaf0; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #0dcaf0; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #0dcaf0; + --bs-gradient: none; } .btn-outline-warning { - --bs-btn-color: #ffc107; - --bs-btn-border-color: #ffc107; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #ffc107; - --bs-btn-hover-border-color: #ffc107; - --bs-btn-focus-shadow-rgb: 255, 193, 7; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #ffc107; - --bs-btn-active-border-color: #ffc107; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #ffc107; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #ffc107; - --bs-gradient: none; + --bs-btn-color: #ffc107; + --bs-btn-border-color: #ffc107; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #ffc107; + --bs-btn-hover-border-color: #ffc107; + --bs-btn-focus-shadow-rgb: 255, 193, 7; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #ffc107; + --bs-btn-active-border-color: #ffc107; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #ffc107; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #ffc107; + --bs-gradient: none; } .btn-outline-danger { - --bs-btn-color: #dc3545; - --bs-btn-border-color: #dc3545; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #dc3545; - --bs-btn-hover-border-color: #dc3545; - --bs-btn-focus-shadow-rgb: 220, 53, 69; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #dc3545; - --bs-btn-active-border-color: #dc3545; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #dc3545; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #dc3545; - --bs-gradient: none; + --bs-btn-color: #dc3545; + --bs-btn-border-color: #dc3545; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #dc3545; + --bs-btn-hover-border-color: #dc3545; + --bs-btn-focus-shadow-rgb: 220, 53, 69; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #dc3545; + --bs-btn-active-border-color: #dc3545; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #dc3545; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #dc3545; + --bs-gradient: none; } .btn-outline-light { - --bs-btn-color: #f8f9fa; - --bs-btn-border-color: #f8f9fa; - --bs-btn-hover-color: #000; - --bs-btn-hover-bg: #f8f9fa; - --bs-btn-hover-border-color: #f8f9fa; - --bs-btn-focus-shadow-rgb: 248, 249, 250; - --bs-btn-active-color: #000; - --bs-btn-active-bg: #f8f9fa; - --bs-btn-active-border-color: #f8f9fa; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #f8f9fa; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #f8f9fa; - --bs-gradient: none; + --bs-btn-color: #f8f9fa; + --bs-btn-border-color: #f8f9fa; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #f8f9fa; + --bs-btn-hover-border-color: #f8f9fa; + --bs-btn-focus-shadow-rgb: 248, 249, 250; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #f8f9fa; + --bs-btn-active-border-color: #f8f9fa; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #f8f9fa; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #f8f9fa; + --bs-gradient: none; } .btn-outline-dark { - --bs-btn-color: #212529; - --bs-btn-border-color: #212529; - --bs-btn-hover-color: #fff; - --bs-btn-hover-bg: #212529; - --bs-btn-hover-border-color: #212529; - --bs-btn-focus-shadow-rgb: 33, 37, 41; - --bs-btn-active-color: #fff; - --bs-btn-active-bg: #212529; - --bs-btn-active-border-color: #212529; - --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - --bs-btn-disabled-color: #212529; - --bs-btn-disabled-bg: transparent; - --bs-btn-disabled-border-color: #212529; - --bs-gradient: none; + --bs-btn-color: #212529; + --bs-btn-border-color: #212529; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #212529; + --bs-btn-hover-border-color: #212529; + --bs-btn-focus-shadow-rgb: 33, 37, 41; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #212529; + --bs-btn-active-border-color: #212529; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #212529; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #212529; + --bs-gradient: none; } .btn-link { - --bs-btn-font-weight: 400; - --bs-btn-color: var(--bs-link-color); - --bs-btn-bg: transparent; - --bs-btn-border-color: transparent; - --bs-btn-hover-color: var(--bs-link-hover-color); - --bs-btn-hover-border-color: transparent; - --bs-btn-active-color: var(--bs-link-hover-color); - --bs-btn-active-border-color: transparent; - --bs-btn-disabled-color: #6c757d; - --bs-btn-disabled-border-color: transparent; - --bs-btn-box-shadow: 0 0 0 #000; - --bs-btn-focus-shadow-rgb: 49, 132, 253; - text-decoration: underline; + --bs-btn-font-weight: 400; + --bs-btn-color: var(--bs-link-color); + --bs-btn-bg: transparent; + --bs-btn-border-color: transparent; + --bs-btn-hover-color: var(--bs-link-hover-color); + --bs-btn-hover-border-color: transparent; + --bs-btn-active-color: var(--bs-link-hover-color); + --bs-btn-active-border-color: transparent; + --bs-btn-disabled-color: #6c757d; + --bs-btn-disabled-border-color: transparent; + --bs-btn-box-shadow: 0 0 0 #000; + --bs-btn-focus-shadow-rgb: 49, 132, 253; + text-decoration: underline; } + .btn-link:focus-visible { - color: var(--bs-btn-color); + color: var(--bs-btn-color); } + .btn-link:hover { - color: var(--bs-btn-hover-color); + color: var(--bs-btn-hover-color); } .btn-lg, .btn-group-lg > .btn { - --bs-btn-padding-y: 0.5rem; - --bs-btn-padding-x: 1rem; - --bs-btn-font-size: 1.25rem; - --bs-btn-border-radius: var(--bs-border-radius-lg); + --bs-btn-padding-y: 0.5rem; + --bs-btn-padding-x: 1rem; + --bs-btn-font-size: 1.25rem; + --bs-btn-border-radius: var(--bs-border-radius-lg); } .btn-sm, .btn-group-sm > .btn { - --bs-btn-padding-y: 0.25rem; - --bs-btn-padding-x: 0.5rem; - --bs-btn-font-size: 0.875rem; - --bs-btn-border-radius: var(--bs-border-radius-sm); + --bs-btn-padding-y: 0.25rem; + --bs-btn-padding-x: 0.5rem; + --bs-btn-font-size: 0.875rem; + --bs-btn-border-radius: var(--bs-border-radius-sm); } .fade { - transition: opacity 0.15s linear; + transition: opacity 0.15s linear; } + @media (prefers-reduced-motion: reduce) { - .fade { - transition: none; - } + .fade { + transition: none; + } } + .fade:not(.show) { - opacity: 0; + opacity: 0; } .collapse:not(.show) { - display: none; + display: none; } .collapsing { - height: 0; - overflow: hidden; - transition: height 0.35s ease; + height: 0; + overflow: hidden; + transition: height 0.35s ease; } + @media (prefers-reduced-motion: reduce) { - .collapsing { - transition: none; - } + .collapsing { + transition: none; + } } + .collapsing.collapse-horizontal { - width: 0; - height: auto; - transition: width 0.35s ease; + width: 0; + height: auto; + transition: width 0.35s ease; } + @media (prefers-reduced-motion: reduce) { - .collapsing.collapse-horizontal { - transition: none; - } + .collapsing.collapse-horizontal { + transition: none; + } } .dropup, @@ -3402,328 +3785,368 @@ textarea.form-control-lg { .dropstart, .dropup-center, .dropdown-center { - position: relative; + position: relative; } .dropdown-toggle { - white-space: nowrap; + white-space: nowrap; } + .dropdown-toggle::after { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid; - border-left: 0.3em solid transparent; - border-bottom: 0; - border-right: 0.3em solid transparent; + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-left: 0.3em solid transparent; + border-bottom: 0; + border-right: 0.3em solid transparent; } + .dropdown-toggle:empty::after { - margin-right: 0; + margin-right: 0; } .dropdown-menu { - --bs-dropdown-zindex: 1000; - --bs-dropdown-min-width: 10rem; - --bs-dropdown-padding-x: 0; - --bs-dropdown-padding-y: 0.5rem; - --bs-dropdown-spacer: 0.125rem; - --bs-dropdown-font-size: 1rem; - --bs-dropdown-color: var(--bs-body-color); - --bs-dropdown-bg: var(--bs-body-bg); - --bs-dropdown-border-color: var(--bs-border-color-translucent); - --bs-dropdown-border-radius: var(--bs-border-radius); - --bs-dropdown-border-width: var(--bs-border-width); - --bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width)); - --bs-dropdown-divider-bg: var(--bs-border-color-translucent); - --bs-dropdown-divider-margin-y: 0.5rem; - --bs-dropdown-box-shadow: var(--bs-box-shadow); - --bs-dropdown-link-color: var(--bs-body-color); - --bs-dropdown-link-hover-color: var(--bs-body-color); - --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg); - --bs-dropdown-link-active-color: #fff; - --bs-dropdown-link-active-bg: #0d6efd; - --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); - --bs-dropdown-item-padding-x: 1rem; - --bs-dropdown-item-padding-y: 0.25rem; - --bs-dropdown-header-color: #6c757d; - --bs-dropdown-header-padding-x: 1rem; - --bs-dropdown-header-padding-y: 0.5rem; - position: absolute; - z-index: var(--bs-dropdown-zindex); - display: none; - min-width: var(--bs-dropdown-min-width); - padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); - margin: 0; - font-size: var(--bs-dropdown-font-size); - color: var(--bs-dropdown-color); - text-align: right; - list-style: none; - background-color: var(--bs-dropdown-bg); - background-clip: padding-box; - border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); - border-radius: var(--bs-dropdown-border-radius); + --bs-dropdown-zindex: 1000; + --bs-dropdown-min-width: 10rem; + --bs-dropdown-padding-x: 0; + --bs-dropdown-padding-y: 0.5rem; + --bs-dropdown-spacer: 0.125rem; + --bs-dropdown-font-size: 1rem; + --bs-dropdown-color: var(--bs-body-color); + --bs-dropdown-bg: var(--bs-body-bg); + --bs-dropdown-border-color: var(--bs-border-color-translucent); + --bs-dropdown-border-radius: var(--bs-border-radius); + --bs-dropdown-border-width: var(--bs-border-width); + --bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width)); + --bs-dropdown-divider-bg: var(--bs-border-color-translucent); + --bs-dropdown-divider-margin-y: 0.5rem; + --bs-dropdown-box-shadow: var(--bs-box-shadow); + --bs-dropdown-link-color: var(--bs-body-color); + --bs-dropdown-link-hover-color: var(--bs-body-color); + --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg); + --bs-dropdown-link-active-color: #fff; + --bs-dropdown-link-active-bg: #0d6efd; + --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); + --bs-dropdown-item-padding-x: 1rem; + --bs-dropdown-item-padding-y: 0.25rem; + --bs-dropdown-header-color: #6c757d; + --bs-dropdown-header-padding-x: 1rem; + --bs-dropdown-header-padding-y: 0.5rem; + position: absolute; + z-index: var(--bs-dropdown-zindex); + display: none; + min-width: var(--bs-dropdown-min-width); + padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); + margin: 0; + font-size: var(--bs-dropdown-font-size); + color: var(--bs-dropdown-color); + text-align: right; + list-style: none; + background-color: var(--bs-dropdown-bg); + background-clip: padding-box; + border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); + border-radius: var(--bs-dropdown-border-radius); } + .dropdown-menu[data-bs-popper] { - top: 100%; - right: 0; - margin-top: var(--bs-dropdown-spacer); + top: 100%; + right: 0; + margin-top: var(--bs-dropdown-spacer); } .dropdown-menu-start { - --bs-position: start; + --bs-position: start; } + .dropdown-menu-start[data-bs-popper] { - left: auto; - right: 0; + left: auto; + right: 0; } .dropdown-menu-end { - --bs-position: end; + --bs-position: end; } + .dropdown-menu-end[data-bs-popper] { - left: 0; - right: auto; + left: 0; + right: auto; } @media (min-width: 576px) { - .dropdown-menu-sm-start { - --bs-position: start; - } - .dropdown-menu-sm-start[data-bs-popper] { - left: auto; - right: 0; - } - .dropdown-menu-sm-end { - --bs-position: end; - } - .dropdown-menu-sm-end[data-bs-popper] { - left: 0; - right: auto; - } + .dropdown-menu-sm-start { + --bs-position: start; + } + + .dropdown-menu-sm-start[data-bs-popper] { + left: auto; + right: 0; + } + + .dropdown-menu-sm-end { + --bs-position: end; + } + + .dropdown-menu-sm-end[data-bs-popper] { + left: 0; + right: auto; + } } + @media (min-width: 768px) { - .dropdown-menu-md-start { - --bs-position: start; - } - .dropdown-menu-md-start[data-bs-popper] { - left: auto; - right: 0; - } - .dropdown-menu-md-end { - --bs-position: end; - } - .dropdown-menu-md-end[data-bs-popper] { - left: 0; - right: auto; - } + .dropdown-menu-md-start { + --bs-position: start; + } + + .dropdown-menu-md-start[data-bs-popper] { + left: auto; + right: 0; + } + + .dropdown-menu-md-end { + --bs-position: end; + } + + .dropdown-menu-md-end[data-bs-popper] { + left: 0; + right: auto; + } } + @media (min-width: 992px) { - .dropdown-menu-lg-start { - --bs-position: start; - } - .dropdown-menu-lg-start[data-bs-popper] { - left: auto; - right: 0; - } - .dropdown-menu-lg-end { - --bs-position: end; - } - .dropdown-menu-lg-end[data-bs-popper] { - left: 0; - right: auto; - } + .dropdown-menu-lg-start { + --bs-position: start; + } + + .dropdown-menu-lg-start[data-bs-popper] { + left: auto; + right: 0; + } + + .dropdown-menu-lg-end { + --bs-position: end; + } + + .dropdown-menu-lg-end[data-bs-popper] { + left: 0; + right: auto; + } } + @media (min-width: 1200px) { - .dropdown-menu-xl-start { - --bs-position: start; - } - .dropdown-menu-xl-start[data-bs-popper] { - left: auto; - right: 0; - } - .dropdown-menu-xl-end { - --bs-position: end; - } - .dropdown-menu-xl-end[data-bs-popper] { - left: 0; - right: auto; - } + .dropdown-menu-xl-start { + --bs-position: start; + } + + .dropdown-menu-xl-start[data-bs-popper] { + left: auto; + right: 0; + } + + .dropdown-menu-xl-end { + --bs-position: end; + } + + .dropdown-menu-xl-end[data-bs-popper] { + left: 0; + right: auto; + } } + @media (min-width: 1400px) { - .dropdown-menu-xxl-start { - --bs-position: start; - } - .dropdown-menu-xxl-start[data-bs-popper] { - left: auto; - right: 0; - } - .dropdown-menu-xxl-end { - --bs-position: end; - } - .dropdown-menu-xxl-end[data-bs-popper] { - left: 0; - right: auto; - } + .dropdown-menu-xxl-start { + --bs-position: start; + } + + .dropdown-menu-xxl-start[data-bs-popper] { + left: auto; + right: 0; + } + + .dropdown-menu-xxl-end { + --bs-position: end; + } + + .dropdown-menu-xxl-end[data-bs-popper] { + left: 0; + right: auto; + } } + .dropup .dropdown-menu[data-bs-popper] { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: var(--bs-dropdown-spacer); + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: var(--bs-dropdown-spacer); } + .dropup .dropdown-toggle::after { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0; - border-left: 0.3em solid transparent; - border-bottom: 0.3em solid; - border-right: 0.3em solid transparent; + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-left: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-right: 0.3em solid transparent; } + .dropup .dropdown-toggle:empty::after { - margin-right: 0; + margin-right: 0; } .dropend .dropdown-menu[data-bs-popper] { - top: 0; - left: auto; - right: 100%; - margin-top: 0; - margin-right: var(--bs-dropdown-spacer); + top: 0; + left: auto; + right: 100%; + margin-top: 0; + margin-right: var(--bs-dropdown-spacer); } + .dropend .dropdown-toggle::after { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-left: 0; - border-bottom: 0.3em solid transparent; - border-right: 0.3em solid; + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-left: 0; + border-bottom: 0.3em solid transparent; + border-right: 0.3em solid; } + .dropend .dropdown-toggle:empty::after { - margin-right: 0; + margin-right: 0; } + .dropend .dropdown-toggle::after { - vertical-align: 0; + vertical-align: 0; } .dropstart .dropdown-menu[data-bs-popper] { - top: 0; - left: 100%; - right: auto; - margin-top: 0; - margin-left: var(--bs-dropdown-spacer); -} -.dropstart .dropdown-toggle::after { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; + top: 0; + left: 100%; + right: auto; + margin-top: 0; + margin-left: var(--bs-dropdown-spacer); +} + +.dropstart .dropdown-toggle::after { + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; } + .dropstart .dropdown-toggle::after { - display: none; + display: none; } + .dropstart .dropdown-toggle::before { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-left: 0.3em solid; - border-bottom: 0.3em solid transparent; + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-left: 0.3em solid; + border-bottom: 0.3em solid transparent; } + .dropstart .dropdown-toggle:empty::after { - margin-right: 0; + margin-right: 0; } + .dropstart .dropdown-toggle::before { - vertical-align: 0; + vertical-align: 0; } .dropdown-divider { - height: 0; - margin: var(--bs-dropdown-divider-margin-y) 0; - overflow: hidden; - border-top: 1px solid var(--bs-dropdown-divider-bg); - opacity: 1; + height: 0; + margin: var(--bs-dropdown-divider-margin-y) 0; + overflow: hidden; + border-top: 1px solid var(--bs-dropdown-divider-bg); + opacity: 1; } .dropdown-item { - display: block; - width: 100%; - padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); - clear: both; - font-weight: 400; - color: var(--bs-dropdown-link-color); - text-align: inherit; - text-decoration: none; - white-space: nowrap; - background-color: transparent; - border: 0; - border-radius: var(--bs-dropdown-item-border-radius, 0); + display: block; + width: 100%; + padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); + clear: both; + font-weight: 400; + color: var(--bs-dropdown-link-color); + text-align: inherit; + text-decoration: none; + white-space: nowrap; + background-color: transparent; + border: 0; + border-radius: var(--bs-dropdown-item-border-radius, 0); } + .dropdown-item:hover, .dropdown-item:focus { - color: var(--bs-dropdown-link-hover-color); - background-color: var(--bs-dropdown-link-hover-bg); + color: var(--bs-dropdown-link-hover-color); + background-color: var(--bs-dropdown-link-hover-bg); } + .dropdown-item.active, .dropdown-item:active { - color: var(--bs-dropdown-link-active-color); - text-decoration: none; - background-color: var(--bs-dropdown-link-active-bg); + color: var(--bs-dropdown-link-active-color); + text-decoration: none; + background-color: var(--bs-dropdown-link-active-bg); } + .dropdown-item.disabled, .dropdown-item:disabled { - color: var(--bs-dropdown-link-disabled-color); - pointer-events: none; - background-color: transparent; + color: var(--bs-dropdown-link-disabled-color); + pointer-events: none; + background-color: transparent; } .dropdown-menu.show { - display: block; + display: block; } .dropdown-header { - display: block; - padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x); - margin-bottom: 0; - font-size: 0.875rem; - color: var(--bs-dropdown-header-color); - white-space: nowrap; + display: block; + padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x); + margin-bottom: 0; + font-size: 0.875rem; + color: var(--bs-dropdown-header-color); + white-space: nowrap; } .dropdown-item-text { - display: block; - padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); - color: var(--bs-dropdown-link-color); + display: block; + padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); + color: var(--bs-dropdown-link-color); } .dropdown-menu-dark { - --bs-dropdown-color: #dee2e6; - --bs-dropdown-bg: #343a40; - --bs-dropdown-border-color: var(--bs-border-color-translucent); - --bs-dropdown-box-shadow: ; - --bs-dropdown-link-color: #dee2e6; - --bs-dropdown-link-hover-color: #fff; - --bs-dropdown-divider-bg: var(--bs-border-color-translucent); - --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15); - --bs-dropdown-link-active-color: #fff; - --bs-dropdown-link-active-bg: #0d6efd; - --bs-dropdown-link-disabled-color: #adb5bd; - --bs-dropdown-header-color: #adb5bd; + --bs-dropdown-color: #dee2e6; + --bs-dropdown-bg: #343a40; + --bs-dropdown-border-color: var(--bs-border-color-translucent); + --bs-dropdown-box-shadow: ; + --bs-dropdown-link-color: #dee2e6; + --bs-dropdown-link-hover-color: #fff; + --bs-dropdown-divider-bg: var(--bs-border-color-translucent); + --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15); + --bs-dropdown-link-active-color: #fff; + --bs-dropdown-link-active-bg: #0d6efd; + --bs-dropdown-link-disabled-color: #adb5bd; + --bs-dropdown-header-color: #adb5bd; } .btn-group, .btn-group-vertical { - position: relative; - display: inline-flex; - vertical-align: middle; + position: relative; + display: inline-flex; + vertical-align: middle; } + .btn-group > .btn, .btn-group-vertical > .btn { - position: relative; - flex: 1 1 auto; + position: relative; + flex: 1 1 auto; } + .btn-group > .btn-check:checked + .btn, .btn-group > .btn-check:focus + .btn, .btn-group > .btn:hover, @@ -3736,246 +4159,271 @@ textarea.form-control-lg { .btn-group-vertical > .btn:focus, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn.active { - z-index: 1; + z-index: 1; } .btn-toolbar { - display: flex; - flex-wrap: wrap; - justify-content: flex-start; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } + .btn-toolbar .input-group { - width: auto; + width: auto; } .btn-group { - border-radius: var(--bs-border-radius); + border-radius: var(--bs-border-radius); } + .btn-group > :not(.btn-check:first-child) + .btn, .btn-group > .btn-group:not(:first-child) { - margin-right: calc(var(--bs-border-width) * -1); + margin-right: calc(var(--bs-border-width) * -1); } + .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn.dropdown-toggle-split:first-child, .btn-group > .btn-group:not(:last-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .btn-group > .btn:nth-child(n+3), .btn-group > :not(.btn-check) + .btn, .btn-group > .btn-group:not(:first-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; } .dropdown-toggle-split { - padding-left: 0.5625rem; - padding-right: 0.5625rem; + padding-left: 0.5625rem; + padding-right: 0.5625rem; } + .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { - margin-right: 0; + margin-right: 0; } + .dropstart .dropdown-toggle-split::before { - margin-left: 0; + margin-left: 0; } .btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { - padding-left: 0.375rem; - padding-right: 0.375rem; + padding-left: 0.375rem; + padding-right: 0.375rem; } .btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { - padding-left: 0.75rem; - padding-right: 0.75rem; + padding-left: 0.75rem; + padding-right: 0.75rem; } .btn-group-vertical { - flex-direction: column; - align-items: flex-start; - justify-content: center; + flex-direction: column; + align-items: flex-start; + justify-content: center; } + .btn-group-vertical > .btn, .btn-group-vertical > .btn-group { - width: 100%; + width: 100%; } + .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) { - margin-top: calc(var(--bs-border-width) * -1); + margin-top: calc(var(--bs-border-width) * -1); } + .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } + .btn-group-vertical > .btn ~ .btn, .btn-group-vertical > .btn-group:not(:first-child) > .btn { - border-top-right-radius: 0; - border-top-left-radius: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; } .nav { - --bs-nav-link-padding-x: 1rem; - --bs-nav-link-padding-y: 0.5rem; - --bs-nav-link-font-weight: ; - --bs-nav-link-color: var(--bs-link-color); - --bs-nav-link-hover-color: var(--bs-link-hover-color); - --bs-nav-link-disabled-color: var(--bs-secondary-color); - display: flex; - flex-wrap: wrap; - padding-right: 0; - margin-bottom: 0; - list-style: none; + --bs-nav-link-padding-x: 1rem; + --bs-nav-link-padding-y: 0.5rem; + --bs-nav-link-font-weight: ; + --bs-nav-link-color: var(--bs-link-color); + --bs-nav-link-hover-color: var(--bs-link-hover-color); + --bs-nav-link-disabled-color: var(--bs-secondary-color); + display: flex; + flex-wrap: wrap; + padding-right: 0; + margin-bottom: 0; + list-style: none; } .nav-link { - display: block; - padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); - font-size: var(--bs-nav-link-font-size); - font-weight: var(--bs-nav-link-font-weight); - color: var(--bs-nav-link-color); - text-decoration: none; - background: none; - border: 0; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; + display: block; + padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); + font-size: var(--bs-nav-link-font-size); + font-weight: var(--bs-nav-link-font-weight); + color: var(--bs-nav-link-color); + text-decoration: none; + background: none; + border: 0; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .nav-link { - transition: none; - } + .nav-link { + transition: none; + } } + .nav-link:hover, .nav-link:focus { - color: var(--bs-nav-link-hover-color); + color: var(--bs-nav-link-hover-color); } + .nav-link:focus-visible { - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } + .nav-link.disabled, .nav-link:disabled { - color: var(--bs-nav-link-disabled-color); - pointer-events: none; - cursor: default; + color: var(--bs-nav-link-disabled-color); + pointer-events: none; + cursor: default; } .nav-tabs { - --bs-nav-tabs-border-width: var(--bs-border-width); - --bs-nav-tabs-border-color: var(--bs-border-color); - --bs-nav-tabs-border-radius: var(--bs-border-radius); - --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color); - --bs-nav-tabs-link-active-color: var(--bs-emphasis-color); - --bs-nav-tabs-link-active-bg: var(--bs-body-bg); - --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg); - border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); + --bs-nav-tabs-border-width: var(--bs-border-width); + --bs-nav-tabs-border-color: var(--bs-border-color); + --bs-nav-tabs-border-radius: var(--bs-border-radius); + --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color); + --bs-nav-tabs-link-active-color: var(--bs-emphasis-color); + --bs-nav-tabs-link-active-bg: var(--bs-body-bg); + --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg); + border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); } + .nav-tabs .nav-link { - margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); - border: var(--bs-nav-tabs-border-width) solid transparent; - border-top-right-radius: var(--bs-nav-tabs-border-radius); - border-top-left-radius: var(--bs-nav-tabs-border-radius); + margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); + border: var(--bs-nav-tabs-border-width) solid transparent; + border-top-right-radius: var(--bs-nav-tabs-border-radius); + border-top-left-radius: var(--bs-nav-tabs-border-radius); } + .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - isolation: isolate; - border-color: var(--bs-nav-tabs-link-hover-border-color); + isolation: isolate; + border-color: var(--bs-nav-tabs-link-hover-border-color); } + .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { - color: var(--bs-nav-tabs-link-active-color); - background-color: var(--bs-nav-tabs-link-active-bg); - border-color: var(--bs-nav-tabs-link-active-border-color); + color: var(--bs-nav-tabs-link-active-color); + background-color: var(--bs-nav-tabs-link-active-bg); + border-color: var(--bs-nav-tabs-link-active-border-color); } + .nav-tabs .dropdown-menu { - margin-top: calc(-1 * var(--bs-nav-tabs-border-width)); - border-top-right-radius: 0; - border-top-left-radius: 0; + margin-top: calc(-1 * var(--bs-nav-tabs-border-width)); + border-top-right-radius: 0; + border-top-left-radius: 0; } .nav-pills { - --bs-nav-pills-border-radius: var(--bs-border-radius); - --bs-nav-pills-link-active-color: #fff; - --bs-nav-pills-link-active-bg: #0d6efd; + --bs-nav-pills-border-radius: var(--bs-border-radius); + --bs-nav-pills-link-active-color: #fff; + --bs-nav-pills-link-active-bg: #0d6efd; } + .nav-pills .nav-link { - border-radius: var(--bs-nav-pills-border-radius); + border-radius: var(--bs-nav-pills-border-radius); } + .nav-pills .nav-link.active, .nav-pills .show > .nav-link { - color: var(--bs-nav-pills-link-active-color); - background-color: var(--bs-nav-pills-link-active-bg); + color: var(--bs-nav-pills-link-active-color); + background-color: var(--bs-nav-pills-link-active-bg); } .nav-underline { - --bs-nav-underline-gap: 1rem; - --bs-nav-underline-border-width: 0.125rem; - --bs-nav-underline-link-active-color: var(--bs-emphasis-color); - gap: var(--bs-nav-underline-gap); + --bs-nav-underline-gap: 1rem; + --bs-nav-underline-border-width: 0.125rem; + --bs-nav-underline-link-active-color: var(--bs-emphasis-color); + gap: var(--bs-nav-underline-gap); } + .nav-underline .nav-link { - padding-left: 0; - padding-right: 0; - border-bottom: var(--bs-nav-underline-border-width) solid transparent; + padding-left: 0; + padding-right: 0; + border-bottom: var(--bs-nav-underline-border-width) solid transparent; } + .nav-underline .nav-link:hover, .nav-underline .nav-link:focus { - border-bottom-color: currentcolor; + border-bottom-color: currentcolor; } + .nav-underline .nav-link.active, .nav-underline .show > .nav-link { - font-weight: 700; - color: var(--bs-nav-underline-link-active-color); - border-bottom-color: currentcolor; + font-weight: 700; + color: var(--bs-nav-underline-link-active-color); + border-bottom-color: currentcolor; } .nav-fill > .nav-link, .nav-fill .nav-item { - flex: 1 1 auto; - text-align: center; + flex: 1 1 auto; + text-align: center; } .nav-justified > .nav-link, .nav-justified .nav-item { - flex-basis: 0; - flex-grow: 1; - text-align: center; + flex-basis: 0; + flex-grow: 1; + text-align: center; } .nav-fill .nav-item .nav-link, .nav-justified .nav-item .nav-link { - width: 100%; + width: 100%; } .tab-content > .tab-pane { - display: none; + display: none; } + .tab-content > .active { - display: block; + display: block; } .navbar { - --bs-navbar-padding-x: 0; - --bs-navbar-padding-y: 0.5rem; - --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65); - --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8); - --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3); - --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1); - --bs-navbar-brand-padding-y: 0.3125rem; - --bs-navbar-brand-margin-end: 1rem; - --bs-navbar-brand-font-size: 1.25rem; - --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1); - --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1); - --bs-navbar-nav-link-padding-x: 0.5rem; - --bs-navbar-toggler-padding-y: 0.25rem; - --bs-navbar-toggler-padding-x: 0.75rem; - --bs-navbar-toggler-font-size: 1.25rem; - --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); - --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15); - --bs-navbar-toggler-border-radius: var(--bs-border-radius); - --bs-navbar-toggler-focus-width: 0.25rem; - --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; - position: relative; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x); + --bs-navbar-padding-x: 0; + --bs-navbar-padding-y: 0.5rem; + --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65); + --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8); + --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3); + --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-brand-padding-y: 0.3125rem; + --bs-navbar-brand-margin-end: 1rem; + --bs-navbar-brand-font-size: 1.25rem; + --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-nav-link-padding-x: 0.5rem; + --bs-navbar-toggler-padding-y: 0.25rem; + --bs-navbar-toggler-padding-x: 0.75rem; + --bs-navbar-toggler-font-size: 1.25rem; + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15); + --bs-navbar-toggler-border-radius: var(--bs-border-radius); + --bs-navbar-toggler-focus-width: 0.25rem; + --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x); } + .navbar > .container, .navbar > .container-fluid, .navbar > .container-sm, @@ -3983,315 +4431,426 @@ textarea.form-control-lg { .navbar > .container-lg, .navbar > .container-xl, .navbar > .container-xxl { - display: flex; - flex-wrap: inherit; - align-items: center; - justify-content: space-between; + display: flex; + flex-wrap: inherit; + align-items: center; + justify-content: space-between; } + .navbar-brand { - padding-top: var(--bs-navbar-brand-padding-y); - padding-bottom: var(--bs-navbar-brand-padding-y); - margin-left: var(--bs-navbar-brand-margin-end); - font-size: var(--bs-navbar-brand-font-size); - color: var(--bs-navbar-brand-color); - text-decoration: none; - white-space: nowrap; + padding-top: var(--bs-navbar-brand-padding-y); + padding-bottom: var(--bs-navbar-brand-padding-y); + margin-left: var(--bs-navbar-brand-margin-end); + font-size: var(--bs-navbar-brand-font-size); + color: var(--bs-navbar-brand-color); + text-decoration: none; + white-space: nowrap; } + .navbar-brand:hover, .navbar-brand:focus { - color: var(--bs-navbar-brand-hover-color); + color: var(--bs-navbar-brand-hover-color); } .navbar-nav { - --bs-nav-link-padding-x: 0; - --bs-nav-link-padding-y: 0.5rem; - --bs-nav-link-font-weight: ; - --bs-nav-link-color: var(--bs-navbar-color); - --bs-nav-link-hover-color: var(--bs-navbar-hover-color); - --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color); - display: flex; - flex-direction: column; - padding-right: 0; - margin-bottom: 0; - list-style: none; + --bs-nav-link-padding-x: 0; + --bs-nav-link-padding-y: 0.5rem; + --bs-nav-link-font-weight: ; + --bs-nav-link-color: var(--bs-navbar-color); + --bs-nav-link-hover-color: var(--bs-navbar-hover-color); + --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color); + display: flex; + flex-direction: column; + padding-right: 0; + margin-bottom: 0; + list-style: none; } + .navbar-nav .nav-link.active, .navbar-nav .nav-link.show { - color: var(--bs-navbar-active-color); + color: var(--bs-navbar-active-color); } + .navbar-nav .dropdown-menu { - position: static; + position: static; } .navbar-text { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: var(--bs-navbar-color); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-navbar-color); } + .navbar-text a, .navbar-text a:hover, .navbar-text a:focus { - color: var(--bs-navbar-active-color); + color: var(--bs-navbar-active-color); } .navbar-collapse { - flex-basis: 100%; - flex-grow: 1; - align-items: center; + flex-basis: 100%; + flex-grow: 1; + align-items: center; } .navbar-toggler { - padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x); - font-size: var(--bs-navbar-toggler-font-size); - line-height: 1; - color: var(--bs-navbar-color); - background-color: transparent; - border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color); - border-radius: var(--bs-navbar-toggler-border-radius); - transition: var(--bs-navbar-toggler-transition); + padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x); + font-size: var(--bs-navbar-toggler-font-size); + line-height: 1; + color: var(--bs-navbar-color); + background-color: transparent; + border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color); + border-radius: var(--bs-navbar-toggler-border-radius); + transition: var(--bs-navbar-toggler-transition); } + @media (prefers-reduced-motion: reduce) { - .navbar-toggler { - transition: none; - } + .navbar-toggler { + transition: none; + } } + .navbar-toggler:hover { - text-decoration: none; + text-decoration: none; } + .navbar-toggler:focus { - text-decoration: none; - outline: 0; - box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width); + text-decoration: none; + outline: 0; + box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width); } .navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - background-image: var(--bs-navbar-toggler-icon-bg); - background-repeat: no-repeat; - background-position: center; - background-size: 100%; + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + background-image: var(--bs-navbar-toggler-icon-bg); + background-repeat: no-repeat; + background-position: center; + background-size: 100%; } .navbar-nav-scroll { - max-height: var(--bs-scroll-height, 75vh); - overflow-y: auto; + max-height: var(--bs-scroll-height, 75vh); + overflow-y: auto; } @media (min-width: 576px) { - .navbar-expand-sm { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-sm .navbar-nav { - flex-direction: row; - } - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-sm .navbar-nav .nav-link { - padding-left: var(--bs-navbar-nav-link-padding-x); - padding-right: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-sm .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-sm .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-sm .navbar-toggler { - display: none; - } - .navbar-expand-sm .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-sm .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-sm .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-sm { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-sm .navbar-nav { + flex-direction: row; + } + + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-sm .navbar-nav .nav-link { + padding-left: var(--bs-navbar-nav-link-padding-x); + padding-right: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-sm .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-sm .navbar-toggler { + display: none; + } + + .navbar-expand-sm .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-sm .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-sm .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 768px) { - .navbar-expand-md { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-md .navbar-nav { - flex-direction: row; - } - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-md .navbar-nav .nav-link { - padding-left: var(--bs-navbar-nav-link-padding-x); - padding-right: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-md .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-md .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-md .navbar-toggler { - display: none; - } - .navbar-expand-md .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-md .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-md .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-md { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-md .navbar-nav { + flex-direction: row; + } + + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-md .navbar-nav .nav-link { + padding-left: var(--bs-navbar-nav-link-padding-x); + padding-right: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-md .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-md .navbar-toggler { + display: none; + } + + .navbar-expand-md .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-md .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-md .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 992px) { - .navbar-expand-lg { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { - flex-direction: row; - } - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-lg .navbar-nav .nav-link { - padding-left: var(--bs-navbar-nav-link-padding-x); - padding-right: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-lg .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-lg .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; - } - .navbar-expand-lg .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-lg .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-lg .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-lg { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-lg .navbar-nav { + flex-direction: row; + } + + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-lg .navbar-nav .nav-link { + padding-left: var(--bs-navbar-nav-link-padding-x); + padding-right: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-lg .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-lg .navbar-toggler { + display: none; + } + + .navbar-expand-lg .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-lg .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-lg .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 1200px) { - .navbar-expand-xl { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-xl .navbar-nav { - flex-direction: row; - } - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xl .navbar-nav .nav-link { - padding-left: var(--bs-navbar-nav-link-padding-x); - padding-right: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-xl .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-xl .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-xl .navbar-toggler { - display: none; - } - .navbar-expand-xl .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; - } - .navbar-expand-xl .offcanvas .offcanvas-header { - display: none; - } - .navbar-expand-xl .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } + .navbar-expand-xl { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-xl .navbar-nav { + flex-direction: row; + } + + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-xl .navbar-nav .nav-link { + padding-left: var(--bs-navbar-nav-link-padding-x); + padding-right: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-xl .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-xl .navbar-toggler { + display: none; + } + + .navbar-expand-xl .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-xl .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-xl .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } + @media (min-width: 1400px) { - .navbar-expand-xxl { + .navbar-expand-xxl { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-xxl .navbar-nav { + flex-direction: row; + } + + .navbar-expand-xxl .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-xxl .navbar-nav .nav-link { + padding-left: var(--bs-navbar-nav-link-padding-x); + padding-right: var(--bs-navbar-nav-link-padding-x); + } + + .navbar-expand-xxl .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-xxl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + + .navbar-expand-xxl .navbar-toggler { + display: none; + } + + .navbar-expand-xxl .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + + .navbar-expand-xxl .offcanvas .offcanvas-header { + display: none; + } + + .navbar-expand-xxl .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } +} + +.navbar-expand { flex-wrap: nowrap; justify-content: flex-start; - } - .navbar-expand-xxl .navbar-nav { +} + +.navbar-expand .navbar-nav { flex-direction: row; - } - .navbar-expand-xxl .navbar-nav .dropdown-menu { +} + +.navbar-expand .navbar-nav .dropdown-menu { position: absolute; - } - .navbar-expand-xxl .navbar-nav .nav-link { +} + +.navbar-expand .navbar-nav .nav-link { padding-left: var(--bs-navbar-nav-link-padding-x); padding-right: var(--bs-navbar-nav-link-padding-x); - } - .navbar-expand-xxl .navbar-nav-scroll { +} + +.navbar-expand .navbar-nav-scroll { overflow: visible; - } - .navbar-expand-xxl .navbar-collapse { +} + +.navbar-expand .navbar-collapse { display: flex !important; flex-basis: auto; - } - .navbar-expand-xxl .navbar-toggler { +} + +.navbar-expand .navbar-toggler { display: none; - } - .navbar-expand-xxl .offcanvas { +} + +.navbar-expand .offcanvas { position: static; z-index: auto; flex-grow: 1; @@ -4302,1991 +4861,2573 @@ textarea.form-control-lg { border: 0 !important; transform: none !important; transition: none; - } - .navbar-expand-xxl .offcanvas .offcanvas-header { +} + +.navbar-expand .offcanvas .offcanvas-header { display: none; - } - .navbar-expand-xxl .offcanvas .offcanvas-body { +} + +.navbar-expand .offcanvas .offcanvas-body { display: flex; flex-grow: 0; padding: 0; overflow-y: visible; - } -} -.navbar-expand { - flex-wrap: nowrap; - justify-content: flex-start; -} -.navbar-expand .navbar-nav { - flex-direction: row; -} -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute; -} -.navbar-expand .navbar-nav .nav-link { - padding-left: var(--bs-navbar-nav-link-padding-x); - padding-right: var(--bs-navbar-nav-link-padding-x); -} -.navbar-expand .navbar-nav-scroll { - overflow: visible; -} -.navbar-expand .navbar-collapse { - display: flex !important; - flex-basis: auto; -} -.navbar-expand .navbar-toggler { - display: none; -} -.navbar-expand .offcanvas { - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - transition: none; -} -.navbar-expand .offcanvas .offcanvas-header { - display: none; -} -.navbar-expand .offcanvas .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; } .navbar-dark, .navbar[data-bs-theme=dark] { - --bs-navbar-color: rgba(255, 255, 255, 0.55); - --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); - --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); - --bs-navbar-active-color: #fff; - --bs-navbar-brand-color: #fff; - --bs-navbar-brand-hover-color: #fff; - --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1); - --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-color: rgba(255, 255, 255, 0.55); + --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); + --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); + --bs-navbar-active-color: #fff; + --bs-navbar-brand-color: #fff; + --bs-navbar-brand-hover-color: #fff; + --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1); + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } [data-bs-theme=dark] .navbar-toggler-icon { - --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } .card { - --bs-card-spacer-y: 1rem; - --bs-card-spacer-x: 1rem; - --bs-card-title-spacer-y: 0.5rem; - --bs-card-title-color: ; - --bs-card-subtitle-color: ; - --bs-card-border-width: var(--bs-border-width); - --bs-card-border-color: var(--bs-border-color-translucent); - --bs-card-border-radius: var(--bs-border-radius); - --bs-card-box-shadow: ; - --bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); - --bs-card-cap-padding-y: 0.5rem; - --bs-card-cap-padding-x: 1rem; - --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03); - --bs-card-cap-color: ; - --bs-card-height: ; - --bs-card-color: ; - --bs-card-bg: var(--bs-body-bg); - --bs-card-img-overlay-padding: 1rem; - --bs-card-group-margin: 0.75rem; - position: relative; - display: flex; - flex-direction: column; - min-width: 0; - height: var(--bs-card-height); - color: var(--bs-body-color); - word-wrap: break-word; - background-color: var(--bs-card-bg); - background-clip: border-box; - border: var(--bs-card-border-width) solid var(--bs-card-border-color); - border-radius: var(--bs-card-border-radius); + --bs-card-spacer-y: 1rem; + --bs-card-spacer-x: 1rem; + --bs-card-title-spacer-y: 0.5rem; + --bs-card-title-color: ; + --bs-card-subtitle-color: ; + --bs-card-border-width: var(--bs-border-width); + --bs-card-border-color: var(--bs-border-color-translucent); + --bs-card-border-radius: var(--bs-border-radius); + --bs-card-box-shadow: ; + --bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); + --bs-card-cap-padding-y: 0.5rem; + --bs-card-cap-padding-x: 1rem; + --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03); + --bs-card-cap-color: ; + --bs-card-height: ; + --bs-card-color: ; + --bs-card-bg: var(--bs-body-bg); + --bs-card-img-overlay-padding: 1rem; + --bs-card-group-margin: 0.75rem; + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + height: var(--bs-card-height); + color: var(--bs-body-color); + word-wrap: break-word; + background-color: var(--bs-card-bg); + background-clip: border-box; + border: var(--bs-card-border-width) solid var(--bs-card-border-color); + border-radius: var(--bs-card-border-radius); } + .card > hr { - margin-left: 0; - margin-right: 0; + margin-left: 0; + margin-right: 0; } + .card > .list-group { - border-top: inherit; - border-bottom: inherit; + border-top: inherit; + border-bottom: inherit; } + .card > .list-group:first-child { - border-top-width: 0; - border-top-right-radius: var(--bs-card-inner-border-radius); - border-top-left-radius: var(--bs-card-inner-border-radius); + border-top-width: 0; + border-top-right-radius: var(--bs-card-inner-border-radius); + border-top-left-radius: var(--bs-card-inner-border-radius); } + .card > .list-group:last-child { - border-bottom-width: 0; - border-bottom-left-radius: var(--bs-card-inner-border-radius); - border-bottom-right-radius: var(--bs-card-inner-border-radius); + border-bottom-width: 0; + border-bottom-left-radius: var(--bs-card-inner-border-radius); + border-bottom-right-radius: var(--bs-card-inner-border-radius); } + .card > .card-header + .list-group, .card > .list-group + .card-footer { - border-top: 0; + border-top: 0; } .card-body { - flex: 1 1 auto; - padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); - color: var(--bs-card-color); + flex: 1 1 auto; + padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); + color: var(--bs-card-color); } .card-title { - margin-bottom: var(--bs-card-title-spacer-y); - color: var(--bs-card-title-color); + margin-bottom: var(--bs-card-title-spacer-y); + color: var(--bs-card-title-color); } .card-subtitle { - margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); - margin-bottom: 0; - color: var(--bs-card-subtitle-color); + margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); + margin-bottom: 0; + color: var(--bs-card-subtitle-color); } .card-text:last-child { - margin-bottom: 0; + margin-bottom: 0; } .card-link + .card-link { - margin-right: var(--bs-card-spacer-x); + margin-right: var(--bs-card-spacer-x); } .card-header { - padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); - margin-bottom: 0; - color: var(--bs-card-cap-color); - background-color: var(--bs-card-cap-bg); - border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color); + padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); + margin-bottom: 0; + color: var(--bs-card-cap-color); + background-color: var(--bs-card-cap-bg); + border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color); } + .card-header:first-child { - border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0; + border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0; } .card-footer { - padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); - color: var(--bs-card-cap-color); - background-color: var(--bs-card-cap-bg); - border-top: var(--bs-card-border-width) solid var(--bs-card-border-color); + padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); + color: var(--bs-card-cap-color); + background-color: var(--bs-card-cap-bg); + border-top: var(--bs-card-border-width) solid var(--bs-card-border-color); } + .card-footer:last-child { - border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius); + border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius); } .card-header-tabs { - margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); - margin-bottom: calc(-1 * var(--bs-card-cap-padding-y)); - margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); - border-bottom: 0; + margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-bottom: calc(-1 * var(--bs-card-cap-padding-y)); + margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); + border-bottom: 0; } + .card-header-tabs .nav-link.active { - background-color: var(--bs-card-bg); - border-bottom-color: var(--bs-card-bg); + background-color: var(--bs-card-bg); + border-bottom-color: var(--bs-card-bg); } .card-header-pills { - margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); - margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); } .card-img-overlay { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - padding: var(--bs-card-img-overlay-padding); - border-radius: var(--bs-card-inner-border-radius); + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + padding: var(--bs-card-img-overlay-padding); + border-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-top, .card-img-bottom { - width: 100%; + width: 100%; } .card-img, .card-img-top { - border-top-right-radius: var(--bs-card-inner-border-radius); - border-top-left-radius: var(--bs-card-inner-border-radius); + border-top-right-radius: var(--bs-card-inner-border-radius); + border-top-left-radius: var(--bs-card-inner-border-radius); } .card-img, .card-img-bottom { - border-bottom-left-radius: var(--bs-card-inner-border-radius); - border-bottom-right-radius: var(--bs-card-inner-border-radius); + border-bottom-left-radius: var(--bs-card-inner-border-radius); + border-bottom-right-radius: var(--bs-card-inner-border-radius); } .card-group > .card { - margin-bottom: var(--bs-card-group-margin); + margin-bottom: var(--bs-card-group-margin); } + @media (min-width: 576px) { - .card-group { - display: flex; - flex-flow: row wrap; - } - .card-group > .card { - flex: 1 0 0%; - margin-bottom: 0; - } - .card-group > .card + .card { - margin-right: 0; - border-right: 0; - } - .card-group > .card:not(:last-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-left-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-left-radius: 0; - } - .card-group > .card:not(:first-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-right-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-right-radius: 0; - } + .card-group { + display: flex; + flex-flow: row wrap; + } + + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; + } + + .card-group > .card + .card { + margin-right: 0; + border-right: 0; + } + + .card-group > .card:not(:last-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-left-radius: 0; + } + + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-left-radius: 0; + } + + .card-group > .card:not(:first-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-right-radius: 0; + } + + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-right-radius: 0; + } } .accordion { - --bs-accordion-color: var(--bs-body-color); - --bs-accordion-bg: var(--bs-body-bg); - --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; - --bs-accordion-border-color: var(--bs-border-color); - --bs-accordion-border-width: var(--bs-border-width); - --bs-accordion-border-radius: var(--bs-border-radius); - --bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); - --bs-accordion-btn-padding-x: 1.25rem; - --bs-accordion-btn-padding-y: 1rem; - --bs-accordion-btn-color: var(--bs-body-color); - --bs-accordion-btn-bg: var(--bs-accordion-bg); - --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); - --bs-accordion-btn-icon-width: 1.25rem; - --bs-accordion-btn-icon-transform: rotate(-180deg); - --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; - --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); - --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); - --bs-accordion-body-padding-x: 1.25rem; - --bs-accordion-body-padding-y: 1rem; - --bs-accordion-active-color: var(--bs-primary-text-emphasis); - --bs-accordion-active-bg: var(--bs-primary-bg-subtle); + --bs-accordion-color: var(--bs-body-color); + --bs-accordion-bg: var(--bs-body-bg); + --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; + --bs-accordion-border-color: var(--bs-border-color); + --bs-accordion-border-width: var(--bs-border-width); + --bs-accordion-border-radius: var(--bs-border-radius); + --bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); + --bs-accordion-btn-padding-x: 1.25rem; + --bs-accordion-btn-padding-y: 1rem; + --bs-accordion-btn-color: var(--bs-body-color); + --bs-accordion-btn-bg: var(--bs-accordion-bg); + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-icon-width: 1.25rem; + --bs-accordion-btn-icon-transform: rotate(-180deg); + --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + --bs-accordion-body-padding-x: 1.25rem; + --bs-accordion-body-padding-y: 1rem; + --bs-accordion-active-color: var(--bs-primary-text-emphasis); + --bs-accordion-active-bg: var(--bs-primary-bg-subtle); } .accordion-button { - position: relative; - display: flex; - align-items: center; - width: 100%; - padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x); - font-size: 1rem; - color: var(--bs-accordion-btn-color); - text-align: right; - background-color: var(--bs-accordion-btn-bg); - border: 0; - border-radius: 0; - overflow-anchor: none; - transition: var(--bs-accordion-transition); + position: relative; + display: flex; + align-items: center; + width: 100%; + padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x); + font-size: 1rem; + color: var(--bs-accordion-btn-color); + text-align: right; + background-color: var(--bs-accordion-btn-bg); + border: 0; + border-radius: 0; + overflow-anchor: none; + transition: var(--bs-accordion-transition); } + @media (prefers-reduced-motion: reduce) { - .accordion-button { - transition: none; - } + .accordion-button { + transition: none; + } } + .accordion-button:not(.collapsed) { - color: var(--bs-accordion-active-color); - background-color: var(--bs-accordion-active-bg); - box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color); + color: var(--bs-accordion-active-color); + background-color: var(--bs-accordion-active-bg); + box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color); } + .accordion-button:not(.collapsed)::after { - background-image: var(--bs-accordion-btn-active-icon); - transform: var(--bs-accordion-btn-icon-transform); + background-image: var(--bs-accordion-btn-active-icon); + transform: var(--bs-accordion-btn-icon-transform); } + .accordion-button::after { - flex-shrink: 0; - width: var(--bs-accordion-btn-icon-width); - height: var(--bs-accordion-btn-icon-width); - margin-right: auto; - content: ""; - background-image: var(--bs-accordion-btn-icon); - background-repeat: no-repeat; - background-size: var(--bs-accordion-btn-icon-width); - transition: var(--bs-accordion-btn-icon-transition); + flex-shrink: 0; + width: var(--bs-accordion-btn-icon-width); + height: var(--bs-accordion-btn-icon-width); + margin-right: auto; + content: ""; + background-image: var(--bs-accordion-btn-icon); + background-repeat: no-repeat; + background-size: var(--bs-accordion-btn-icon-width); + transition: var(--bs-accordion-btn-icon-transition); } + @media (prefers-reduced-motion: reduce) { - .accordion-button::after { - transition: none; - } + .accordion-button::after { + transition: none; + } } + .accordion-button:hover { - z-index: 2; + z-index: 2; } + .accordion-button:focus { - z-index: 3; - outline: 0; - box-shadow: var(--bs-accordion-btn-focus-box-shadow); + z-index: 3; + outline: 0; + box-shadow: var(--bs-accordion-btn-focus-box-shadow); } .accordion-header { - margin-bottom: 0; + margin-bottom: 0; } .accordion-item { - color: var(--bs-accordion-color); - background-color: var(--bs-accordion-bg); - border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color); + color: var(--bs-accordion-color); + background-color: var(--bs-accordion-bg); + border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color); } + .accordion-item:first-of-type { - border-top-right-radius: var(--bs-accordion-border-radius); - border-top-left-radius: var(--bs-accordion-border-radius); + border-top-right-radius: var(--bs-accordion-border-radius); + border-top-left-radius: var(--bs-accordion-border-radius); } + .accordion-item:first-of-type > .accordion-header .accordion-button { - border-top-right-radius: var(--bs-accordion-inner-border-radius); - border-top-left-radius: var(--bs-accordion-inner-border-radius); + border-top-right-radius: var(--bs-accordion-inner-border-radius); + border-top-left-radius: var(--bs-accordion-inner-border-radius); } + .accordion-item:not(:first-of-type) { - border-top: 0; + border-top: 0; } + .accordion-item:last-of-type { - border-bottom-left-radius: var(--bs-accordion-border-radius); - border-bottom-right-radius: var(--bs-accordion-border-radius); + border-bottom-left-radius: var(--bs-accordion-border-radius); + border-bottom-right-radius: var(--bs-accordion-border-radius); } + .accordion-item:last-of-type > .accordion-header .accordion-button.collapsed { - border-bottom-left-radius: var(--bs-accordion-inner-border-radius); - border-bottom-right-radius: var(--bs-accordion-inner-border-radius); + border-bottom-left-radius: var(--bs-accordion-inner-border-radius); + border-bottom-right-radius: var(--bs-accordion-inner-border-radius); } + .accordion-item:last-of-type > .accordion-collapse { - border-bottom-left-radius: var(--bs-accordion-border-radius); - border-bottom-right-radius: var(--bs-accordion-border-radius); + border-bottom-left-radius: var(--bs-accordion-border-radius); + border-bottom-right-radius: var(--bs-accordion-border-radius); } .accordion-body { - padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); + padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); } .accordion-flush > .accordion-item { - border-left: 0; - border-right: 0; - border-radius: 0; + border-left: 0; + border-right: 0; + border-radius: 0; } + .accordion-flush > .accordion-item:first-child { - border-top: 0; + border-top: 0; } + .accordion-flush > .accordion-item:last-child { - border-bottom: 0; + border-bottom: 0; } + .accordion-flush > .accordion-item > .accordion-header .accordion-button, .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed { - border-radius: 0; + border-radius: 0; } + .accordion-flush > .accordion-item > .accordion-collapse { - border-radius: 0; + border-radius: 0; } [data-bs-theme=dark] .accordion-button::after { - --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); - --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } .breadcrumb { - --bs-breadcrumb-padding-x: 0; - --bs-breadcrumb-padding-y: 0; - --bs-breadcrumb-margin-bottom: 1rem; - --bs-breadcrumb-bg: ; - --bs-breadcrumb-border-radius: ; - --bs-breadcrumb-divider-color: var(--bs-secondary-color); - --bs-breadcrumb-item-padding-x: 0.5rem; - --bs-breadcrumb-item-active-color: var(--bs-secondary-color); - display: flex; - flex-wrap: wrap; - padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); - margin-bottom: var(--bs-breadcrumb-margin-bottom); - font-size: var(--bs-breadcrumb-font-size); - list-style: none; - background-color: var(--bs-breadcrumb-bg); - border-radius: var(--bs-breadcrumb-border-radius); + --bs-breadcrumb-padding-x: 0; + --bs-breadcrumb-padding-y: 0; + --bs-breadcrumb-margin-bottom: 1rem; + --bs-breadcrumb-bg: ; + --bs-breadcrumb-border-radius: ; + --bs-breadcrumb-divider-color: var(--bs-secondary-color); + --bs-breadcrumb-item-padding-x: 0.5rem; + --bs-breadcrumb-item-active-color: var(--bs-secondary-color); + display: flex; + flex-wrap: wrap; + padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); + margin-bottom: var(--bs-breadcrumb-margin-bottom); + font-size: var(--bs-breadcrumb-font-size); + list-style: none; + background-color: var(--bs-breadcrumb-bg); + border-radius: var(--bs-breadcrumb-border-radius); } .breadcrumb-item + .breadcrumb-item { - padding-right: var(--bs-breadcrumb-item-padding-x); + padding-right: var(--bs-breadcrumb-item-padding-x); } + .breadcrumb-item + .breadcrumb-item::before { - float: right; - padding-left: var(--bs-breadcrumb-item-padding-x); - color: var(--bs-breadcrumb-divider-color); - content: var(--bs-breadcrumb-divider, "/") ; + float: right; + padding-left: var(--bs-breadcrumb-item-padding-x); + color: var(--bs-breadcrumb-divider-color); + content: var(--bs-breadcrumb-divider, "/"); } + .breadcrumb-item.active { - color: var(--bs-breadcrumb-item-active-color); + color: var(--bs-breadcrumb-item-active-color); } .pagination { - --bs-pagination-padding-x: 0.75rem; - --bs-pagination-padding-y: 0.375rem; - --bs-pagination-font-size: 1rem; - --bs-pagination-color: var(--bs-link-color); - --bs-pagination-bg: var(--bs-body-bg); - --bs-pagination-border-width: var(--bs-border-width); - --bs-pagination-border-color: var(--bs-border-color); - --bs-pagination-border-radius: var(--bs-border-radius); - --bs-pagination-hover-color: var(--bs-link-hover-color); - --bs-pagination-hover-bg: var(--bs-tertiary-bg); - --bs-pagination-hover-border-color: var(--bs-border-color); - --bs-pagination-focus-color: var(--bs-link-hover-color); - --bs-pagination-focus-bg: var(--bs-secondary-bg); - --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); - --bs-pagination-active-color: #fff; - --bs-pagination-active-bg: #0d6efd; - --bs-pagination-active-border-color: #0d6efd; - --bs-pagination-disabled-color: var(--bs-secondary-color); - --bs-pagination-disabled-bg: var(--bs-secondary-bg); - --bs-pagination-disabled-border-color: var(--bs-border-color); - display: flex; - padding-right: 0; - list-style: none; + --bs-pagination-padding-x: 0.75rem; + --bs-pagination-padding-y: 0.375rem; + --bs-pagination-font-size: 1rem; + --bs-pagination-color: var(--bs-link-color); + --bs-pagination-bg: var(--bs-body-bg); + --bs-pagination-border-width: var(--bs-border-width); + --bs-pagination-border-color: var(--bs-border-color); + --bs-pagination-border-radius: var(--bs-border-radius); + --bs-pagination-hover-color: var(--bs-link-hover-color); + --bs-pagination-hover-bg: var(--bs-tertiary-bg); + --bs-pagination-hover-border-color: var(--bs-border-color); + --bs-pagination-focus-color: var(--bs-link-hover-color); + --bs-pagination-focus-bg: var(--bs-secondary-bg); + --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + --bs-pagination-active-color: #fff; + --bs-pagination-active-bg: #0d6efd; + --bs-pagination-active-border-color: #0d6efd; + --bs-pagination-disabled-color: var(--bs-secondary-color); + --bs-pagination-disabled-bg: var(--bs-secondary-bg); + --bs-pagination-disabled-border-color: var(--bs-border-color); + display: flex; + padding-right: 0; + list-style: none; } .page-link { - position: relative; - display: block; - padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x); - font-size: var(--bs-pagination-font-size); - color: var(--bs-pagination-color); - text-decoration: none; - background-color: var(--bs-pagination-bg); - border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color); - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + position: relative; + display: block; + padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x); + font-size: var(--bs-pagination-font-size); + color: var(--bs-pagination-color); + text-decoration: none; + background-color: var(--bs-pagination-bg); + border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color); + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .page-link { - transition: none; - } + .page-link { + transition: none; + } } + .page-link:hover { - z-index: 2; - color: var(--bs-pagination-hover-color); - background-color: var(--bs-pagination-hover-bg); - border-color: var(--bs-pagination-hover-border-color); + z-index: 2; + color: var(--bs-pagination-hover-color); + background-color: var(--bs-pagination-hover-bg); + border-color: var(--bs-pagination-hover-border-color); } + .page-link:focus { - z-index: 3; - color: var(--bs-pagination-focus-color); - background-color: var(--bs-pagination-focus-bg); - outline: 0; - box-shadow: var(--bs-pagination-focus-box-shadow); + z-index: 3; + color: var(--bs-pagination-focus-color); + background-color: var(--bs-pagination-focus-bg); + outline: 0; + box-shadow: var(--bs-pagination-focus-box-shadow); } + .page-link.active, .active > .page-link { - z-index: 3; - color: var(--bs-pagination-active-color); - background-color: var(--bs-pagination-active-bg); - border-color: var(--bs-pagination-active-border-color); + z-index: 3; + color: var(--bs-pagination-active-color); + background-color: var(--bs-pagination-active-bg); + border-color: var(--bs-pagination-active-border-color); } + .page-link.disabled, .disabled > .page-link { - color: var(--bs-pagination-disabled-color); - pointer-events: none; - background-color: var(--bs-pagination-disabled-bg); - border-color: var(--bs-pagination-disabled-border-color); + color: var(--bs-pagination-disabled-color); + pointer-events: none; + background-color: var(--bs-pagination-disabled-bg); + border-color: var(--bs-pagination-disabled-border-color); } .page-item:not(:first-child) .page-link { - margin-right: calc(var(--bs-border-width) * -1); + margin-right: calc(var(--bs-border-width) * -1); } + .page-item:first-child .page-link { - border-top-right-radius: var(--bs-pagination-border-radius); - border-bottom-right-radius: var(--bs-pagination-border-radius); + border-top-right-radius: var(--bs-pagination-border-radius); + border-bottom-right-radius: var(--bs-pagination-border-radius); } + .page-item:last-child .page-link { - border-top-left-radius: var(--bs-pagination-border-radius); - border-bottom-left-radius: var(--bs-pagination-border-radius); + border-top-left-radius: var(--bs-pagination-border-radius); + border-bottom-left-radius: var(--bs-pagination-border-radius); } .pagination-lg { - --bs-pagination-padding-x: 1.5rem; - --bs-pagination-padding-y: 0.75rem; - --bs-pagination-font-size: 1.25rem; - --bs-pagination-border-radius: var(--bs-border-radius-lg); + --bs-pagination-padding-x: 1.5rem; + --bs-pagination-padding-y: 0.75rem; + --bs-pagination-font-size: 1.25rem; + --bs-pagination-border-radius: var(--bs-border-radius-lg); } .pagination-sm { - --bs-pagination-padding-x: 0.5rem; - --bs-pagination-padding-y: 0.25rem; - --bs-pagination-font-size: 0.875rem; - --bs-pagination-border-radius: var(--bs-border-radius-sm); + --bs-pagination-padding-x: 0.5rem; + --bs-pagination-padding-y: 0.25rem; + --bs-pagination-font-size: 0.875rem; + --bs-pagination-border-radius: var(--bs-border-radius-sm); } .badge { - --bs-badge-padding-x: 0.65em; - --bs-badge-padding-y: 0.35em; - --bs-badge-font-size: 0.75em; - --bs-badge-font-weight: 700; - --bs-badge-color: #fff; - --bs-badge-border-radius: var(--bs-border-radius); - display: inline-block; - padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); - font-size: var(--bs-badge-font-size); - font-weight: var(--bs-badge-font-weight); - line-height: 1; - color: var(--bs-badge-color); - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: var(--bs-badge-border-radius); + --bs-badge-padding-x: 0.65em; + --bs-badge-padding-y: 0.35em; + --bs-badge-font-size: 0.75em; + --bs-badge-font-weight: 700; + --bs-badge-color: #fff; + --bs-badge-border-radius: var(--bs-border-radius); + display: inline-block; + padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); + font-size: var(--bs-badge-font-size); + font-weight: var(--bs-badge-font-weight); + line-height: 1; + color: var(--bs-badge-color); + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: var(--bs-badge-border-radius); } + .badge:empty { - display: none; + display: none; } .btn .badge { - position: relative; - top: -1px; + position: relative; + top: -1px; } .alert { - --bs-alert-bg: transparent; - --bs-alert-padding-x: 1rem; - --bs-alert-padding-y: 1rem; - --bs-alert-margin-bottom: 1rem; - --bs-alert-color: inherit; - --bs-alert-border-color: transparent; - --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color); - --bs-alert-border-radius: var(--bs-border-radius); - --bs-alert-link-color: inherit; - position: relative; - padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); - margin-bottom: var(--bs-alert-margin-bottom); - color: var(--bs-alert-color); - background-color: var(--bs-alert-bg); - border: var(--bs-alert-border); - border-radius: var(--bs-alert-border-radius); + --bs-alert-bg: transparent; + --bs-alert-padding-x: 1rem; + --bs-alert-padding-y: 1rem; + --bs-alert-margin-bottom: 1rem; + --bs-alert-color: inherit; + --bs-alert-border-color: transparent; + --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color); + --bs-alert-border-radius: var(--bs-border-radius); + --bs-alert-link-color: inherit; + position: relative; + padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); + margin-bottom: var(--bs-alert-margin-bottom); + color: var(--bs-alert-color); + background-color: var(--bs-alert-bg); + border: var(--bs-alert-border); + border-radius: var(--bs-alert-border-radius); } .alert-heading { - color: inherit; + color: inherit; } .alert-link { - font-weight: 700; - color: var(--bs-alert-link-color); + font-weight: 700; + color: var(--bs-alert-link-color); } .alert-dismissible { - padding-left: 3rem; + padding-left: 3rem; } + .alert-dismissible .btn-close { - position: absolute; - top: 0; - left: 0; - z-index: 2; - padding: 1.25rem 1rem; + position: absolute; + top: 0; + left: 0; + z-index: 2; + padding: 1.25rem 1rem; } .alert-primary { - --bs-alert-color: var(--bs-primary-text-emphasis); - --bs-alert-bg: var(--bs-primary-bg-subtle); - --bs-alert-border-color: var(--bs-primary-border-subtle); - --bs-alert-link-color: var(--bs-primary-text-emphasis); + --bs-alert-color: var(--bs-primary-text-emphasis); + --bs-alert-bg: var(--bs-primary-bg-subtle); + --bs-alert-border-color: var(--bs-primary-border-subtle); + --bs-alert-link-color: var(--bs-primary-text-emphasis); } .alert-secondary { - --bs-alert-color: var(--bs-secondary-text-emphasis); - --bs-alert-bg: var(--bs-secondary-bg-subtle); - --bs-alert-border-color: var(--bs-secondary-border-subtle); - --bs-alert-link-color: var(--bs-secondary-text-emphasis); + --bs-alert-color: var(--bs-secondary-text-emphasis); + --bs-alert-bg: var(--bs-secondary-bg-subtle); + --bs-alert-border-color: var(--bs-secondary-border-subtle); + --bs-alert-link-color: var(--bs-secondary-text-emphasis); } .alert-success { - --bs-alert-color: var(--bs-success-text-emphasis); - --bs-alert-bg: var(--bs-success-bg-subtle); - --bs-alert-border-color: var(--bs-success-border-subtle); - --bs-alert-link-color: var(--bs-success-text-emphasis); + --bs-alert-color: var(--bs-success-text-emphasis); + --bs-alert-bg: var(--bs-success-bg-subtle); + --bs-alert-border-color: var(--bs-success-border-subtle); + --bs-alert-link-color: var(--bs-success-text-emphasis); } .alert-info { - --bs-alert-color: var(--bs-info-text-emphasis); - --bs-alert-bg: var(--bs-info-bg-subtle); - --bs-alert-border-color: var(--bs-info-border-subtle); - --bs-alert-link-color: var(--bs-info-text-emphasis); + --bs-alert-color: var(--bs-info-text-emphasis); + --bs-alert-bg: var(--bs-info-bg-subtle); + --bs-alert-border-color: var(--bs-info-border-subtle); + --bs-alert-link-color: var(--bs-info-text-emphasis); } .alert-warning { - --bs-alert-color: var(--bs-warning-text-emphasis); - --bs-alert-bg: var(--bs-warning-bg-subtle); - --bs-alert-border-color: var(--bs-warning-border-subtle); - --bs-alert-link-color: var(--bs-warning-text-emphasis); + --bs-alert-color: var(--bs-warning-text-emphasis); + --bs-alert-bg: var(--bs-warning-bg-subtle); + --bs-alert-border-color: var(--bs-warning-border-subtle); + --bs-alert-link-color: var(--bs-warning-text-emphasis); } .alert-danger { - --bs-alert-color: var(--bs-danger-text-emphasis); - --bs-alert-bg: var(--bs-danger-bg-subtle); - --bs-alert-border-color: var(--bs-danger-border-subtle); - --bs-alert-link-color: var(--bs-danger-text-emphasis); + --bs-alert-color: var(--bs-danger-text-emphasis); + --bs-alert-bg: var(--bs-danger-bg-subtle); + --bs-alert-border-color: var(--bs-danger-border-subtle); + --bs-alert-link-color: var(--bs-danger-text-emphasis); } .alert-light { - --bs-alert-color: var(--bs-light-text-emphasis); - --bs-alert-bg: var(--bs-light-bg-subtle); - --bs-alert-border-color: var(--bs-light-border-subtle); - --bs-alert-link-color: var(--bs-light-text-emphasis); + --bs-alert-color: var(--bs-light-text-emphasis); + --bs-alert-bg: var(--bs-light-bg-subtle); + --bs-alert-border-color: var(--bs-light-border-subtle); + --bs-alert-link-color: var(--bs-light-text-emphasis); } .alert-dark { - --bs-alert-color: var(--bs-dark-text-emphasis); - --bs-alert-bg: var(--bs-dark-bg-subtle); - --bs-alert-border-color: var(--bs-dark-border-subtle); - --bs-alert-link-color: var(--bs-dark-text-emphasis); + --bs-alert-color: var(--bs-dark-text-emphasis); + --bs-alert-bg: var(--bs-dark-bg-subtle); + --bs-alert-border-color: var(--bs-dark-border-subtle); + --bs-alert-link-color: var(--bs-dark-text-emphasis); } @keyframes progress-bar-stripes { - 0% { - background-position-x: 1rem; - } + 0% { + background-position-x: 1rem; + } } + .progress, .progress-stacked { - --bs-progress-height: 1rem; - --bs-progress-font-size: 0.75rem; - --bs-progress-bg: var(--bs-secondary-bg); - --bs-progress-border-radius: var(--bs-border-radius); - --bs-progress-box-shadow: var(--bs-box-shadow-inset); - --bs-progress-bar-color: #fff; - --bs-progress-bar-bg: #0d6efd; - --bs-progress-bar-transition: width 0.6s ease; - display: flex; - height: var(--bs-progress-height); - overflow: hidden; - font-size: var(--bs-progress-font-size); - background-color: var(--bs-progress-bg); - border-radius: var(--bs-progress-border-radius); + --bs-progress-height: 1rem; + --bs-progress-font-size: 0.75rem; + --bs-progress-bg: var(--bs-secondary-bg); + --bs-progress-border-radius: var(--bs-border-radius); + --bs-progress-box-shadow: var(--bs-box-shadow-inset); + --bs-progress-bar-color: #fff; + --bs-progress-bar-bg: #0d6efd; + --bs-progress-bar-transition: width 0.6s ease; + display: flex; + height: var(--bs-progress-height); + overflow: hidden; + font-size: var(--bs-progress-font-size); + background-color: var(--bs-progress-bg); + border-radius: var(--bs-progress-border-radius); } .progress-bar { - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; - color: var(--bs-progress-bar-color); - text-align: center; - white-space: nowrap; - background-color: var(--bs-progress-bar-bg); - transition: var(--bs-progress-bar-transition); + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + color: var(--bs-progress-bar-color); + text-align: center; + white-space: nowrap; + background-color: var(--bs-progress-bar-bg); + transition: var(--bs-progress-bar-transition); } + @media (prefers-reduced-motion: reduce) { - .progress-bar { - transition: none; - } + .progress-bar { + transition: none; + } } .progress-bar-striped { - background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: var(--bs-progress-height) var(--bs-progress-height); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: var(--bs-progress-height) var(--bs-progress-height); } .progress-stacked > .progress { - overflow: visible; + overflow: visible; } .progress-stacked > .progress > .progress-bar { - width: 100%; + width: 100%; } .progress-bar-animated { - animation: 1s linear infinite progress-bar-stripes; + animation: 1s linear infinite progress-bar-stripes; } + @media (prefers-reduced-motion: reduce) { - .progress-bar-animated { - animation: none; - } + .progress-bar-animated { + animation: none; + } } .list-group { - --bs-list-group-color: var(--bs-body-color); - --bs-list-group-bg: var(--bs-body-bg); - --bs-list-group-border-color: var(--bs-border-color); - --bs-list-group-border-width: var(--bs-border-width); - --bs-list-group-border-radius: var(--bs-border-radius); - --bs-list-group-item-padding-x: 1rem; - --bs-list-group-item-padding-y: 0.5rem; - --bs-list-group-action-color: var(--bs-secondary-color); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-tertiary-bg); - --bs-list-group-action-active-color: var(--bs-body-color); - --bs-list-group-action-active-bg: var(--bs-secondary-bg); - --bs-list-group-disabled-color: var(--bs-secondary-color); - --bs-list-group-disabled-bg: var(--bs-body-bg); - --bs-list-group-active-color: #fff; - --bs-list-group-active-bg: #0d6efd; - --bs-list-group-active-border-color: #0d6efd; - display: flex; - flex-direction: column; - padding-right: 0; - margin-bottom: 0; - border-radius: var(--bs-list-group-border-radius); + --bs-list-group-color: var(--bs-body-color); + --bs-list-group-bg: var(--bs-body-bg); + --bs-list-group-border-color: var(--bs-border-color); + --bs-list-group-border-width: var(--bs-border-width); + --bs-list-group-border-radius: var(--bs-border-radius); + --bs-list-group-item-padding-x: 1rem; + --bs-list-group-item-padding-y: 0.5rem; + --bs-list-group-action-color: var(--bs-secondary-color); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-tertiary-bg); + --bs-list-group-action-active-color: var(--bs-body-color); + --bs-list-group-action-active-bg: var(--bs-secondary-bg); + --bs-list-group-disabled-color: var(--bs-secondary-color); + --bs-list-group-disabled-bg: var(--bs-body-bg); + --bs-list-group-active-color: #fff; + --bs-list-group-active-bg: #0d6efd; + --bs-list-group-active-border-color: #0d6efd; + display: flex; + flex-direction: column; + padding-right: 0; + margin-bottom: 0; + border-radius: var(--bs-list-group-border-radius); } .list-group-numbered { - list-style-type: none; - counter-reset: section; + list-style-type: none; + counter-reset: section; } + .list-group-numbered > .list-group-item::before { - content: counters(section, ".") ". "; - counter-increment: section; + content: counters(section, ".") ". "; + counter-increment: section; } .list-group-item-action { - width: 100%; - color: var(--bs-list-group-action-color); - text-align: inherit; + width: 100%; + color: var(--bs-list-group-action-color); + text-align: inherit; } + .list-group-item-action:hover, .list-group-item-action:focus { - z-index: 1; - color: var(--bs-list-group-action-hover-color); - text-decoration: none; - background-color: var(--bs-list-group-action-hover-bg); + z-index: 1; + color: var(--bs-list-group-action-hover-color); + text-decoration: none; + background-color: var(--bs-list-group-action-hover-bg); } + .list-group-item-action:active { - color: var(--bs-list-group-action-active-color); - background-color: var(--bs-list-group-action-active-bg); + color: var(--bs-list-group-action-active-color); + background-color: var(--bs-list-group-action-active-bg); } .list-group-item { - position: relative; - display: block; - padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x); - color: var(--bs-list-group-color); - text-decoration: none; - background-color: var(--bs-list-group-bg); - border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color); + position: relative; + display: block; + padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x); + color: var(--bs-list-group-color); + text-decoration: none; + background-color: var(--bs-list-group-bg); + border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color); } + .list-group-item:first-child { - border-top-right-radius: inherit; - border-top-left-radius: inherit; + border-top-right-radius: inherit; + border-top-left-radius: inherit; } + .list-group-item:last-child { - border-bottom-left-radius: inherit; - border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; + border-bottom-right-radius: inherit; } + .list-group-item.disabled, .list-group-item:disabled { - color: var(--bs-list-group-disabled-color); - pointer-events: none; - background-color: var(--bs-list-group-disabled-bg); + color: var(--bs-list-group-disabled-color); + pointer-events: none; + background-color: var(--bs-list-group-disabled-bg); } + .list-group-item.active { - z-index: 2; - color: var(--bs-list-group-active-color); - background-color: var(--bs-list-group-active-bg); - border-color: var(--bs-list-group-active-border-color); + z-index: 2; + color: var(--bs-list-group-active-color); + background-color: var(--bs-list-group-active-bg); + border-color: var(--bs-list-group-active-border-color); } + .list-group-item + .list-group-item { - border-top-width: 0; + border-top-width: 0; } + .list-group-item + .list-group-item.active { - margin-top: calc(-1 * var(--bs-list-group-border-width)); - border-top-width: var(--bs-list-group-border-width); + margin-top: calc(-1 * var(--bs-list-group-border-width)); + border-top-width: var(--bs-list-group-border-width); } .list-group-horizontal { - flex-direction: row; + flex-direction: row; } + .list-group-horizontal > .list-group-item:first-child:not(:last-child) { - border-bottom-right-radius: var(--bs-list-group-border-radius); - border-top-left-radius: 0; + border-bottom-right-radius: var(--bs-list-group-border-radius); + border-top-left-radius: 0; } + .list-group-horizontal > .list-group-item:last-child:not(:first-child) { - border-top-left-radius: var(--bs-list-group-border-radius); - border-bottom-right-radius: 0; + border-top-left-radius: var(--bs-list-group-border-radius); + border-bottom-right-radius: 0; } + .list-group-horizontal > .list-group-item.active { - margin-top: 0; + margin-top: 0; } + .list-group-horizontal > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-right-width: 0; + border-top-width: var(--bs-list-group-border-width); + border-right-width: 0; } + .list-group-horizontal > .list-group-item + .list-group-item.active { - margin-right: calc(-1 * var(--bs-list-group-border-width)); - border-right-width: var(--bs-list-group-border-width); + margin-right: calc(-1 * var(--bs-list-group-border-width)); + border-right-width: var(--bs-list-group-border-width); } @media (min-width: 576px) { - .list-group-horizontal-sm { - flex-direction: row; - } - .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) { - border-bottom-right-radius: var(--bs-list-group-border-radius); - border-top-left-radius: 0; - } - .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) { - border-top-left-radius: var(--bs-list-group-border-radius); - border-bottom-right-radius: 0; - } - .list-group-horizontal-sm > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-sm > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-right-width: 0; - } - .list-group-horizontal-sm > .list-group-item + .list-group-item.active { - margin-right: calc(-1 * var(--bs-list-group-border-width)); - border-right-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-sm { + flex-direction: row; + } + + .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) { + border-bottom-right-radius: var(--bs-list-group-border-radius); + border-top-left-radius: 0; + } + + .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) { + border-top-left-radius: var(--bs-list-group-border-radius); + border-bottom-right-radius: 0; + } + + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-right-width: 0; + } + + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-right: calc(-1 * var(--bs-list-group-border-width)); + border-right-width: var(--bs-list-group-border-width); + } } + @media (min-width: 768px) { - .list-group-horizontal-md { - flex-direction: row; - } - .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) { - border-bottom-right-radius: var(--bs-list-group-border-radius); - border-top-left-radius: 0; - } - .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) { - border-top-left-radius: var(--bs-list-group-border-radius); - border-bottom-right-radius: 0; - } - .list-group-horizontal-md > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-md > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-right-width: 0; - } - .list-group-horizontal-md > .list-group-item + .list-group-item.active { - margin-right: calc(-1 * var(--bs-list-group-border-width)); - border-right-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-md { + flex-direction: row; + } + + .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) { + border-bottom-right-radius: var(--bs-list-group-border-radius); + border-top-left-radius: 0; + } + + .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) { + border-top-left-radius: var(--bs-list-group-border-radius); + border-bottom-right-radius: 0; + } + + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-right-width: 0; + } + + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-right: calc(-1 * var(--bs-list-group-border-width)); + border-right-width: var(--bs-list-group-border-width); + } } + @media (min-width: 992px) { - .list-group-horizontal-lg { - flex-direction: row; - } - .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) { - border-bottom-right-radius: var(--bs-list-group-border-radius); - border-top-left-radius: 0; - } - .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) { - border-top-left-radius: var(--bs-list-group-border-radius); - border-bottom-right-radius: 0; - } - .list-group-horizontal-lg > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-lg > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-right-width: 0; - } - .list-group-horizontal-lg > .list-group-item + .list-group-item.active { - margin-right: calc(-1 * var(--bs-list-group-border-width)); - border-right-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-lg { + flex-direction: row; + } + + .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) { + border-bottom-right-radius: var(--bs-list-group-border-radius); + border-top-left-radius: 0; + } + + .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) { + border-top-left-radius: var(--bs-list-group-border-radius); + border-bottom-right-radius: 0; + } + + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-right-width: 0; + } + + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-right: calc(-1 * var(--bs-list-group-border-width)); + border-right-width: var(--bs-list-group-border-width); + } } + @media (min-width: 1200px) { - .list-group-horizontal-xl { - flex-direction: row; - } - .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) { - border-bottom-right-radius: var(--bs-list-group-border-radius); - border-top-left-radius: 0; - } - .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) { - border-top-left-radius: var(--bs-list-group-border-radius); - border-bottom-right-radius: 0; - } - .list-group-horizontal-xl > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-xl > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-right-width: 0; - } - .list-group-horizontal-xl > .list-group-item + .list-group-item.active { - margin-right: calc(-1 * var(--bs-list-group-border-width)); - border-right-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-xl { + flex-direction: row; + } + + .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) { + border-bottom-right-radius: var(--bs-list-group-border-radius); + border-top-left-radius: 0; + } + + .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) { + border-top-left-radius: var(--bs-list-group-border-radius); + border-bottom-right-radius: 0; + } + + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-right-width: 0; + } + + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-right: calc(-1 * var(--bs-list-group-border-width)); + border-right-width: var(--bs-list-group-border-width); + } } + @media (min-width: 1400px) { - .list-group-horizontal-xxl { - flex-direction: row; - } - .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) { - border-bottom-right-radius: var(--bs-list-group-border-radius); - border-top-left-radius: 0; - } - .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) { - border-top-left-radius: var(--bs-list-group-border-radius); - border-bottom-right-radius: 0; - } - .list-group-horizontal-xxl > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-xxl > .list-group-item + .list-group-item { - border-top-width: var(--bs-list-group-border-width); - border-right-width: 0; - } - .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { - margin-right: calc(-1 * var(--bs-list-group-border-width)); - border-right-width: var(--bs-list-group-border-width); - } + .list-group-horizontal-xxl { + flex-direction: row; + } + + .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) { + border-bottom-right-radius: var(--bs-list-group-border-radius); + border-top-left-radius: 0; + } + + .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) { + border-top-left-radius: var(--bs-list-group-border-radius); + border-bottom-right-radius: 0; + } + + .list-group-horizontal-xxl > .list-group-item.active { + margin-top: 0; + } + + .list-group-horizontal-xxl > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-right-width: 0; + } + + .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { + margin-right: calc(-1 * var(--bs-list-group-border-width)); + border-right-width: var(--bs-list-group-border-width); + } } + .list-group-flush { - border-radius: 0; + border-radius: 0; } + .list-group-flush > .list-group-item { - border-width: 0 0 var(--bs-list-group-border-width); + border-width: 0 0 var(--bs-list-group-border-width); } + .list-group-flush > .list-group-item:last-child { - border-bottom-width: 0; + border-bottom-width: 0; } .list-group-item-primary { - --bs-list-group-color: var(--bs-primary-text-emphasis); - --bs-list-group-bg: var(--bs-primary-bg-subtle); - --bs-list-group-border-color: var(--bs-primary-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-primary-border-subtle); - --bs-list-group-active-color: var(--bs-primary-bg-subtle); - --bs-list-group-active-bg: var(--bs-primary-text-emphasis); - --bs-list-group-active-border-color: var(--bs-primary-text-emphasis); + --bs-list-group-color: var(--bs-primary-text-emphasis); + --bs-list-group-bg: var(--bs-primary-bg-subtle); + --bs-list-group-border-color: var(--bs-primary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-primary-border-subtle); + --bs-list-group-active-color: var(--bs-primary-bg-subtle); + --bs-list-group-active-bg: var(--bs-primary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-primary-text-emphasis); } .list-group-item-secondary { - --bs-list-group-color: var(--bs-secondary-text-emphasis); - --bs-list-group-bg: var(--bs-secondary-bg-subtle); - --bs-list-group-border-color: var(--bs-secondary-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle); - --bs-list-group-active-color: var(--bs-secondary-bg-subtle); - --bs-list-group-active-bg: var(--bs-secondary-text-emphasis); - --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis); + --bs-list-group-color: var(--bs-secondary-text-emphasis); + --bs-list-group-bg: var(--bs-secondary-bg-subtle); + --bs-list-group-border-color: var(--bs-secondary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle); + --bs-list-group-active-color: var(--bs-secondary-bg-subtle); + --bs-list-group-active-bg: var(--bs-secondary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis); } .list-group-item-success { - --bs-list-group-color: var(--bs-success-text-emphasis); - --bs-list-group-bg: var(--bs-success-bg-subtle); - --bs-list-group-border-color: var(--bs-success-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-success-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-success-border-subtle); - --bs-list-group-active-color: var(--bs-success-bg-subtle); - --bs-list-group-active-bg: var(--bs-success-text-emphasis); - --bs-list-group-active-border-color: var(--bs-success-text-emphasis); + --bs-list-group-color: var(--bs-success-text-emphasis); + --bs-list-group-bg: var(--bs-success-bg-subtle); + --bs-list-group-border-color: var(--bs-success-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-success-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-success-border-subtle); + --bs-list-group-active-color: var(--bs-success-bg-subtle); + --bs-list-group-active-bg: var(--bs-success-text-emphasis); + --bs-list-group-active-border-color: var(--bs-success-text-emphasis); } .list-group-item-info { - --bs-list-group-color: var(--bs-info-text-emphasis); - --bs-list-group-bg: var(--bs-info-bg-subtle); - --bs-list-group-border-color: var(--bs-info-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-info-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-info-border-subtle); - --bs-list-group-active-color: var(--bs-info-bg-subtle); - --bs-list-group-active-bg: var(--bs-info-text-emphasis); - --bs-list-group-active-border-color: var(--bs-info-text-emphasis); + --bs-list-group-color: var(--bs-info-text-emphasis); + --bs-list-group-bg: var(--bs-info-bg-subtle); + --bs-list-group-border-color: var(--bs-info-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-info-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-info-border-subtle); + --bs-list-group-active-color: var(--bs-info-bg-subtle); + --bs-list-group-active-bg: var(--bs-info-text-emphasis); + --bs-list-group-active-border-color: var(--bs-info-text-emphasis); } .list-group-item-warning { - --bs-list-group-color: var(--bs-warning-text-emphasis); - --bs-list-group-bg: var(--bs-warning-bg-subtle); - --bs-list-group-border-color: var(--bs-warning-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-warning-border-subtle); - --bs-list-group-active-color: var(--bs-warning-bg-subtle); - --bs-list-group-active-bg: var(--bs-warning-text-emphasis); - --bs-list-group-active-border-color: var(--bs-warning-text-emphasis); + --bs-list-group-color: var(--bs-warning-text-emphasis); + --bs-list-group-bg: var(--bs-warning-bg-subtle); + --bs-list-group-border-color: var(--bs-warning-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-warning-border-subtle); + --bs-list-group-active-color: var(--bs-warning-bg-subtle); + --bs-list-group-active-bg: var(--bs-warning-text-emphasis); + --bs-list-group-active-border-color: var(--bs-warning-text-emphasis); } .list-group-item-danger { - --bs-list-group-color: var(--bs-danger-text-emphasis); - --bs-list-group-bg: var(--bs-danger-bg-subtle); - --bs-list-group-border-color: var(--bs-danger-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-danger-border-subtle); - --bs-list-group-active-color: var(--bs-danger-bg-subtle); - --bs-list-group-active-bg: var(--bs-danger-text-emphasis); - --bs-list-group-active-border-color: var(--bs-danger-text-emphasis); + --bs-list-group-color: var(--bs-danger-text-emphasis); + --bs-list-group-bg: var(--bs-danger-bg-subtle); + --bs-list-group-border-color: var(--bs-danger-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-danger-border-subtle); + --bs-list-group-active-color: var(--bs-danger-bg-subtle); + --bs-list-group-active-bg: var(--bs-danger-text-emphasis); + --bs-list-group-active-border-color: var(--bs-danger-text-emphasis); } .list-group-item-light { - --bs-list-group-color: var(--bs-light-text-emphasis); - --bs-list-group-bg: var(--bs-light-bg-subtle); - --bs-list-group-border-color: var(--bs-light-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-light-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-light-border-subtle); - --bs-list-group-active-color: var(--bs-light-bg-subtle); - --bs-list-group-active-bg: var(--bs-light-text-emphasis); - --bs-list-group-active-border-color: var(--bs-light-text-emphasis); + --bs-list-group-color: var(--bs-light-text-emphasis); + --bs-list-group-bg: var(--bs-light-bg-subtle); + --bs-list-group-border-color: var(--bs-light-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-light-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-light-border-subtle); + --bs-list-group-active-color: var(--bs-light-bg-subtle); + --bs-list-group-active-bg: var(--bs-light-text-emphasis); + --bs-list-group-active-border-color: var(--bs-light-text-emphasis); } .list-group-item-dark { - --bs-list-group-color: var(--bs-dark-text-emphasis); - --bs-list-group-bg: var(--bs-dark-bg-subtle); - --bs-list-group-border-color: var(--bs-dark-border-subtle); - --bs-list-group-action-hover-color: var(--bs-emphasis-color); - --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle); - --bs-list-group-action-active-color: var(--bs-emphasis-color); - --bs-list-group-action-active-bg: var(--bs-dark-border-subtle); - --bs-list-group-active-color: var(--bs-dark-bg-subtle); - --bs-list-group-active-bg: var(--bs-dark-text-emphasis); - --bs-list-group-active-border-color: var(--bs-dark-text-emphasis); + --bs-list-group-color: var(--bs-dark-text-emphasis); + --bs-list-group-bg: var(--bs-dark-bg-subtle); + --bs-list-group-border-color: var(--bs-dark-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-dark-border-subtle); + --bs-list-group-active-color: var(--bs-dark-bg-subtle); + --bs-list-group-active-bg: var(--bs-dark-text-emphasis); + --bs-list-group-active-border-color: var(--bs-dark-text-emphasis); } .btn-close { - --bs-btn-close-color: #000; - --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); - --bs-btn-close-opacity: 0.5; - --bs-btn-close-hover-opacity: 0.75; - --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); - --bs-btn-close-focus-opacity: 1; - --bs-btn-close-disabled-opacity: 0.25; - --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); - box-sizing: content-box; - width: 1em; - height: 1em; - padding: 0.25em 0.25em; - color: var(--bs-btn-close-color); - background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; - border: 0; - border-radius: 0.375rem; - opacity: var(--bs-btn-close-opacity); + --bs-btn-close-color: #000; + --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); + --bs-btn-close-opacity: 0.5; + --bs-btn-close-hover-opacity: 0.75; + --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + --bs-btn-close-focus-opacity: 1; + --bs-btn-close-disabled-opacity: 0.25; + --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); + box-sizing: content-box; + width: 1em; + height: 1em; + padding: 0.25em 0.25em; + color: var(--bs-btn-close-color); + background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; + border: 0; + border-radius: 0.375rem; + opacity: var(--bs-btn-close-opacity); } + .btn-close:hover { - color: var(--bs-btn-close-color); - text-decoration: none; - opacity: var(--bs-btn-close-hover-opacity); + color: var(--bs-btn-close-color); + text-decoration: none; + opacity: var(--bs-btn-close-hover-opacity); } + .btn-close:focus { - outline: 0; - box-shadow: var(--bs-btn-close-focus-shadow); - opacity: var(--bs-btn-close-focus-opacity); + outline: 0; + box-shadow: var(--bs-btn-close-focus-shadow); + opacity: var(--bs-btn-close-focus-opacity); } + .btn-close:disabled, .btn-close.disabled { - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - opacity: var(--bs-btn-close-disabled-opacity); + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + opacity: var(--bs-btn-close-disabled-opacity); } .btn-close-white { - filter: var(--bs-btn-close-white-filter); + filter: var(--bs-btn-close-white-filter); } [data-bs-theme=dark] .btn-close { - filter: var(--bs-btn-close-white-filter); + filter: var(--bs-btn-close-white-filter); } .toast { - --bs-toast-zindex: 1090; - --bs-toast-padding-x: 0.75rem; - --bs-toast-padding-y: 0.5rem; - --bs-toast-spacing: 1.5rem; - --bs-toast-max-width: 350px; - --bs-toast-font-size: 0.875rem; - --bs-toast-color: ; - --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85); - --bs-toast-border-width: var(--bs-border-width); - --bs-toast-border-color: var(--bs-border-color-translucent); - --bs-toast-border-radius: var(--bs-border-radius); - --bs-toast-box-shadow: var(--bs-box-shadow); - --bs-toast-header-color: var(--bs-secondary-color); - --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85); - --bs-toast-header-border-color: var(--bs-border-color-translucent); - width: var(--bs-toast-max-width); - max-width: 100%; - font-size: var(--bs-toast-font-size); - color: var(--bs-toast-color); - pointer-events: auto; - background-color: var(--bs-toast-bg); - background-clip: padding-box; - border: var(--bs-toast-border-width) solid var(--bs-toast-border-color); - box-shadow: var(--bs-toast-box-shadow); - border-radius: var(--bs-toast-border-radius); + --bs-toast-zindex: 1090; + --bs-toast-padding-x: 0.75rem; + --bs-toast-padding-y: 0.5rem; + --bs-toast-spacing: 1.5rem; + --bs-toast-max-width: 350px; + --bs-toast-font-size: 0.875rem; + --bs-toast-color: ; + --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-border-width: var(--bs-border-width); + --bs-toast-border-color: var(--bs-border-color-translucent); + --bs-toast-border-radius: var(--bs-border-radius); + --bs-toast-box-shadow: var(--bs-box-shadow); + --bs-toast-header-color: var(--bs-secondary-color); + --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-header-border-color: var(--bs-border-color-translucent); + width: var(--bs-toast-max-width); + max-width: 100%; + font-size: var(--bs-toast-font-size); + color: var(--bs-toast-color); + pointer-events: auto; + background-color: var(--bs-toast-bg); + background-clip: padding-box; + border: var(--bs-toast-border-width) solid var(--bs-toast-border-color); + box-shadow: var(--bs-toast-box-shadow); + border-radius: var(--bs-toast-border-radius); } + .toast.showing { - opacity: 0; + opacity: 0; } + .toast:not(.show) { - display: none; + display: none; } .toast-container { - --bs-toast-zindex: 1090; - position: absolute; - z-index: var(--bs-toast-zindex); - width: -webkit-max-content; - width: -moz-max-content; - width: max-content; - max-width: 100%; - pointer-events: none; + --bs-toast-zindex: 1090; + position: absolute; + z-index: var(--bs-toast-zindex); + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + max-width: 100%; + pointer-events: none; } + .toast-container > :not(:last-child) { - margin-bottom: var(--bs-toast-spacing); + margin-bottom: var(--bs-toast-spacing); } .toast-header { - display: flex; - align-items: center; - padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); - color: var(--bs-toast-header-color); - background-color: var(--bs-toast-header-bg); - background-clip: padding-box; - border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color); - border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); - border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); + display: flex; + align-items: center; + padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); + color: var(--bs-toast-header-color); + background-color: var(--bs-toast-header-bg); + background-clip: padding-box; + border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color); + border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); + border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); } + .toast-header .btn-close { - margin-left: calc(-0.5 * var(--bs-toast-padding-x)); - margin-right: var(--bs-toast-padding-x); + margin-left: calc(-0.5 * var(--bs-toast-padding-x)); + margin-right: var(--bs-toast-padding-x); } .toast-body { - padding: var(--bs-toast-padding-x); - word-wrap: break-word; + padding: var(--bs-toast-padding-x); + word-wrap: break-word; } .modal { - --bs-modal-zindex: 1055; - --bs-modal-width: 500px; - --bs-modal-padding: 1rem; - --bs-modal-margin: 0.5rem; - --bs-modal-color: ; - --bs-modal-bg: var(--bs-body-bg); - --bs-modal-border-color: var(--bs-border-color-translucent); - --bs-modal-border-width: var(--bs-border-width); - --bs-modal-border-radius: var(--bs-border-radius-lg); - --bs-modal-box-shadow: var(--bs-box-shadow-sm); - --bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width))); - --bs-modal-header-padding-x: 1rem; - --bs-modal-header-padding-y: 1rem; - --bs-modal-header-padding: 1rem 1rem; - --bs-modal-header-border-color: var(--bs-border-color); - --bs-modal-header-border-width: var(--bs-border-width); - --bs-modal-title-line-height: 1.5; - --bs-modal-footer-gap: 0.5rem; - --bs-modal-footer-bg: ; - --bs-modal-footer-border-color: var(--bs-border-color); - --bs-modal-footer-border-width: var(--bs-border-width); - position: fixed; - top: 0; - right: 0; - z-index: var(--bs-modal-zindex); - display: none; - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - outline: 0; + --bs-modal-zindex: 1055; + --bs-modal-width: 500px; + --bs-modal-padding: 1rem; + --bs-modal-margin: 0.5rem; + --bs-modal-color: ; + --bs-modal-bg: var(--bs-body-bg); + --bs-modal-border-color: var(--bs-border-color-translucent); + --bs-modal-border-width: var(--bs-border-width); + --bs-modal-border-radius: var(--bs-border-radius-lg); + --bs-modal-box-shadow: var(--bs-box-shadow-sm); + --bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width))); + --bs-modal-header-padding-x: 1rem; + --bs-modal-header-padding-y: 1rem; + --bs-modal-header-padding: 1rem 1rem; + --bs-modal-header-border-color: var(--bs-border-color); + --bs-modal-header-border-width: var(--bs-border-width); + --bs-modal-title-line-height: 1.5; + --bs-modal-footer-gap: 0.5rem; + --bs-modal-footer-bg: ; + --bs-modal-footer-border-color: var(--bs-border-color); + --bs-modal-footer-border-width: var(--bs-border-width); + position: fixed; + top: 0; + right: 0; + z-index: var(--bs-modal-zindex); + display: none; + width: 100%; + height: 100%; + overflow-x: hidden; + overflow-y: auto; + outline: 0; } .modal-dialog { - position: relative; - width: auto; - margin: var(--bs-modal-margin); - pointer-events: none; + position: relative; + width: auto; + margin: var(--bs-modal-margin); + pointer-events: none; } + .modal.fade .modal-dialog { - transition: transform 0.3s ease-out; - transform: translate(0, -50px); + transition: transform 0.3s ease-out; + transform: translate(0, -50px); } + @media (prefers-reduced-motion: reduce) { - .modal.fade .modal-dialog { - transition: none; - } + .modal.fade .modal-dialog { + transition: none; + } } + .modal.show .modal-dialog { - transform: none; + transform: none; } + .modal.modal-static .modal-dialog { - transform: scale(1.02); + transform: scale(1.02); } .modal-dialog-scrollable { - height: calc(100% - var(--bs-modal-margin) * 2); + height: calc(100% - var(--bs-modal-margin) * 2); } + .modal-dialog-scrollable .modal-content { - max-height: 100%; - overflow: hidden; + max-height: 100%; + overflow: hidden; } + .modal-dialog-scrollable .modal-body { - overflow-y: auto; + overflow-y: auto; } .modal-dialog-centered { - display: flex; - align-items: center; - min-height: calc(100% - var(--bs-modal-margin) * 2); + display: flex; + align-items: center; + min-height: calc(100% - var(--bs-modal-margin) * 2); } .modal-content { - position: relative; - display: flex; - flex-direction: column; - width: 100%; - color: var(--bs-modal-color); - pointer-events: auto; - background-color: var(--bs-modal-bg); - background-clip: padding-box; - border: var(--bs-modal-border-width) solid var(--bs-modal-border-color); - border-radius: var(--bs-modal-border-radius); - outline: 0; + position: relative; + display: flex; + flex-direction: column; + width: 100%; + color: var(--bs-modal-color); + pointer-events: auto; + background-color: var(--bs-modal-bg); + background-clip: padding-box; + border: var(--bs-modal-border-width) solid var(--bs-modal-border-color); + border-radius: var(--bs-modal-border-radius); + outline: 0; } .modal-backdrop { - --bs-backdrop-zindex: 1050; - --bs-backdrop-bg: #000; - --bs-backdrop-opacity: 0.5; - position: fixed; - top: 0; - right: 0; - z-index: var(--bs-backdrop-zindex); - width: 100vw; - height: 100vh; - background-color: var(--bs-backdrop-bg); + --bs-backdrop-zindex: 1050; + --bs-backdrop-bg: #000; + --bs-backdrop-opacity: 0.5; + position: fixed; + top: 0; + right: 0; + z-index: var(--bs-backdrop-zindex); + width: 100vw; + height: 100vh; + background-color: var(--bs-backdrop-bg); } + .modal-backdrop.fade { - opacity: 0; + opacity: 0; } + .modal-backdrop.show { - opacity: var(--bs-backdrop-opacity); + opacity: var(--bs-backdrop-opacity); } .modal-header { - display: flex; - flex-shrink: 0; - align-items: center; - padding: var(--bs-modal-header-padding); - border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); - border-top-right-radius: var(--bs-modal-inner-border-radius); - border-top-left-radius: var(--bs-modal-inner-border-radius); + display: flex; + flex-shrink: 0; + align-items: center; + padding: var(--bs-modal-header-padding); + border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); + border-top-right-radius: var(--bs-modal-inner-border-radius); + border-top-left-radius: var(--bs-modal-inner-border-radius); } + .modal-header .btn-close { - padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5); - margin: calc(-0.5 * var(--bs-modal-header-padding-y)) auto calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)); + padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5); + margin: calc(-0.5 * var(--bs-modal-header-padding-y)) auto calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)); } .modal-title { - margin-bottom: 0; - line-height: var(--bs-modal-title-line-height); + margin-bottom: 0; + line-height: var(--bs-modal-title-line-height); } .modal-body { - position: relative; - flex: 1 1 auto; - padding: var(--bs-modal-padding); + position: relative; + flex: 1 1 auto; + padding: var(--bs-modal-padding); } .modal-footer { - display: flex; - flex-shrink: 0; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; - padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5); - background-color: var(--bs-modal-footer-bg); - border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); - border-bottom-left-radius: var(--bs-modal-inner-border-radius); - border-bottom-right-radius: var(--bs-modal-inner-border-radius); + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5); + background-color: var(--bs-modal-footer-bg); + border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); + border-bottom-left-radius: var(--bs-modal-inner-border-radius); + border-bottom-right-radius: var(--bs-modal-inner-border-radius); } + .modal-footer > * { - margin: calc(var(--bs-modal-footer-gap) * 0.5); + margin: calc(var(--bs-modal-footer-gap) * 0.5); } @media (min-width: 576px) { - .modal { - --bs-modal-margin: 1.75rem; - --bs-modal-box-shadow: var(--bs-box-shadow); - } - .modal-dialog { - max-width: var(--bs-modal-width); - margin-left: auto; - margin-right: auto; - } - .modal-sm { - --bs-modal-width: 300px; - } + .modal { + --bs-modal-margin: 1.75rem; + --bs-modal-box-shadow: var(--bs-box-shadow); + } + + .modal-dialog { + max-width: var(--bs-modal-width); + margin-left: auto; + margin-right: auto; + } + + .modal-sm { + --bs-modal-width: 300px; + } } + @media (min-width: 992px) { - .modal-lg, - .modal-xl { - --bs-modal-width: 800px; - } + .modal-lg, + .modal-xl { + --bs-modal-width: 800px; + } } + @media (min-width: 1200px) { - .modal-xl { - --bs-modal-width: 1140px; - } + .modal-xl { + --bs-modal-width: 1140px; + } } + .modal-fullscreen { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; + width: 100vw; + max-width: none; + height: 100%; + margin: 0; } + .modal-fullscreen .modal-content { - height: 100%; - border: 0; - border-radius: 0; -} -.modal-fullscreen .modal-header, + height: 100%; + border: 0; + border-radius: 0; +} + +.modal-fullscreen .modal-header, .modal-fullscreen .modal-footer { - border-radius: 0; + border-radius: 0; } + .modal-fullscreen .modal-body { - overflow-y: auto; + overflow-y: auto; } @media (max-width: 575.98px) { - .modal-fullscreen-sm-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-sm-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-sm-down .modal-header, - .modal-fullscreen-sm-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-sm-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-sm-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-sm-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-sm-down .modal-header, + .modal-fullscreen-sm-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-sm-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 767.98px) { - .modal-fullscreen-md-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-md-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-md-down .modal-header, - .modal-fullscreen-md-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-md-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-md-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-md-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-md-down .modal-header, + .modal-fullscreen-md-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-md-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 991.98px) { - .modal-fullscreen-lg-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-lg-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-lg-down .modal-header, - .modal-fullscreen-lg-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-lg-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-lg-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-lg-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-lg-down .modal-header, + .modal-fullscreen-lg-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-lg-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 1199.98px) { - .modal-fullscreen-xl-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-xl-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-xl-down .modal-header, - .modal-fullscreen-xl-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-xl-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-xl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-xl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-xl-down .modal-header, + .modal-fullscreen-xl-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-xl-down .modal-body { + overflow-y: auto; + } } + @media (max-width: 1399.98px) { - .modal-fullscreen-xxl-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-xxl-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-xxl-down .modal-header, - .modal-fullscreen-xxl-down .modal-footer { - border-radius: 0; - } - .modal-fullscreen-xxl-down .modal-body { - overflow-y: auto; - } + .modal-fullscreen-xxl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-xxl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-xxl-down .modal-header, + .modal-fullscreen-xxl-down .modal-footer { + border-radius: 0; + } + + .modal-fullscreen-xxl-down .modal-body { + overflow-y: auto; + } } + .tooltip { - --bs-tooltip-zindex: 1080; - --bs-tooltip-max-width: 200px; - --bs-tooltip-padding-x: 0.5rem; - --bs-tooltip-padding-y: 0.25rem; - --bs-tooltip-margin: ; - --bs-tooltip-font-size: 0.875rem; - --bs-tooltip-color: var(--bs-body-bg); - --bs-tooltip-bg: var(--bs-emphasis-color); - --bs-tooltip-border-radius: var(--bs-border-radius); - --bs-tooltip-opacity: 0.9; - --bs-tooltip-arrow-width: 0.8rem; - --bs-tooltip-arrow-height: 0.4rem; - z-index: var(--bs-tooltip-zindex); - display: block; - margin: var(--bs-tooltip-margin); - font-family: var(--bs-font-sans-serif); - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: right; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - white-space: normal; - word-spacing: normal; - line-break: auto; - font-size: var(--bs-tooltip-font-size); - word-wrap: break-word; - opacity: 0; + --bs-tooltip-zindex: 1080; + --bs-tooltip-max-width: 200px; + --bs-tooltip-padding-x: 0.5rem; + --bs-tooltip-padding-y: 0.25rem; + --bs-tooltip-margin: ; + --bs-tooltip-font-size: 0.875rem; + --bs-tooltip-color: var(--bs-body-bg); + --bs-tooltip-bg: var(--bs-emphasis-color); + --bs-tooltip-border-radius: var(--bs-border-radius); + --bs-tooltip-opacity: 0.9; + --bs-tooltip-arrow-width: 0.8rem; + --bs-tooltip-arrow-height: 0.4rem; + z-index: var(--bs-tooltip-zindex); + display: block; + margin: var(--bs-tooltip-margin); + font-family: var(--bs-font-sans-serif); + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: right; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + white-space: normal; + word-spacing: normal; + line-break: auto; + font-size: var(--bs-tooltip-font-size); + word-wrap: break-word; + opacity: 0; } + .tooltip.show { - opacity: var(--bs-tooltip-opacity); + opacity: var(--bs-tooltip-opacity); } + .tooltip .tooltip-arrow { - display: block; - width: var(--bs-tooltip-arrow-width); - height: var(--bs-tooltip-arrow-height); + display: block; + width: var(--bs-tooltip-arrow-width); + height: var(--bs-tooltip-arrow-height); } + .tooltip .tooltip-arrow::before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid; + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; } .bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { - bottom: calc(-1 * var(--bs-tooltip-arrow-height)); + bottom: calc(-1 * var(--bs-tooltip-arrow-height)); } + .bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { - top: -1px; - border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; - border-top-color: var(--bs-tooltip-bg); + top: -1px; + border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; + border-top-color: var(--bs-tooltip-bg); } + .bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { - left: calc(-1 * var(--bs-tooltip-arrow-height)); - width: var(--bs-tooltip-arrow-height); - height: var(--bs-tooltip-arrow-width); + left: calc(-1 * var(--bs-tooltip-arrow-height)); + width: var(--bs-tooltip-arrow-height); + height: var(--bs-tooltip-arrow-width); } + .bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before { - right: -1px; - border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; - border-right-color: var(--bs-tooltip-bg); + right: -1px; + border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; + border-right-color: var(--bs-tooltip-bg); } + .bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { - top: calc(-1 * var(--bs-tooltip-arrow-height)); + top: calc(-1 * var(--bs-tooltip-arrow-height)); } + .bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { - bottom: -1px; - border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); - border-bottom-color: var(--bs-tooltip-bg); + bottom: -1px; + border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); + border-bottom-color: var(--bs-tooltip-bg); } + .bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { - right: calc(-1 * var(--bs-tooltip-arrow-height)); - width: var(--bs-tooltip-arrow-height); - height: var(--bs-tooltip-arrow-width); + right: calc(-1 * var(--bs-tooltip-arrow-height)); + width: var(--bs-tooltip-arrow-height); + height: var(--bs-tooltip-arrow-width); } + .bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before { - left: -1px; - border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); - border-left-color: var(--bs-tooltip-bg); + left: -1px; + border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); + border-left-color: var(--bs-tooltip-bg); } + .tooltip-inner { - max-width: var(--bs-tooltip-max-width); - padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x); - color: var(--bs-tooltip-color); - text-align: center; - background-color: var(--bs-tooltip-bg); - border-radius: var(--bs-tooltip-border-radius); + max-width: var(--bs-tooltip-max-width); + padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x); + color: var(--bs-tooltip-color); + text-align: center; + background-color: var(--bs-tooltip-bg); + border-radius: var(--bs-tooltip-border-radius); } .popover { - --bs-popover-zindex: 1070; - --bs-popover-max-width: 276px; - --bs-popover-font-size: 0.875rem; - --bs-popover-bg: var(--bs-body-bg); - --bs-popover-border-width: var(--bs-border-width); - --bs-popover-border-color: var(--bs-border-color-translucent); - --bs-popover-border-radius: var(--bs-border-radius-lg); - --bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width)); - --bs-popover-box-shadow: var(--bs-box-shadow); - --bs-popover-header-padding-x: 1rem; - --bs-popover-header-padding-y: 0.5rem; - --bs-popover-header-font-size: 1rem; - --bs-popover-header-color: inherit; - --bs-popover-header-bg: var(--bs-secondary-bg); - --bs-popover-body-padding-x: 1rem; - --bs-popover-body-padding-y: 1rem; - --bs-popover-body-color: var(--bs-body-color); - --bs-popover-arrow-width: 1rem; - --bs-popover-arrow-height: 0.5rem; - --bs-popover-arrow-border: var(--bs-popover-border-color); - z-index: var(--bs-popover-zindex); - display: block; - max-width: var(--bs-popover-max-width); - font-family: var(--bs-font-sans-serif); - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: right; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - white-space: normal; - word-spacing: normal; - line-break: auto; - font-size: var(--bs-popover-font-size); - word-wrap: break-word; - background-color: var(--bs-popover-bg); - background-clip: padding-box; - border: var(--bs-popover-border-width) solid var(--bs-popover-border-color); - border-radius: var(--bs-popover-border-radius); + --bs-popover-zindex: 1070; + --bs-popover-max-width: 276px; + --bs-popover-font-size: 0.875rem; + --bs-popover-bg: var(--bs-body-bg); + --bs-popover-border-width: var(--bs-border-width); + --bs-popover-border-color: var(--bs-border-color-translucent); + --bs-popover-border-radius: var(--bs-border-radius-lg); + --bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width)); + --bs-popover-box-shadow: var(--bs-box-shadow); + --bs-popover-header-padding-x: 1rem; + --bs-popover-header-padding-y: 0.5rem; + --bs-popover-header-font-size: 1rem; + --bs-popover-header-color: inherit; + --bs-popover-header-bg: var(--bs-secondary-bg); + --bs-popover-body-padding-x: 1rem; + --bs-popover-body-padding-y: 1rem; + --bs-popover-body-color: var(--bs-body-color); + --bs-popover-arrow-width: 1rem; + --bs-popover-arrow-height: 0.5rem; + --bs-popover-arrow-border: var(--bs-popover-border-color); + z-index: var(--bs-popover-zindex); + display: block; + max-width: var(--bs-popover-max-width); + font-family: var(--bs-font-sans-serif); + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: right; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + white-space: normal; + word-spacing: normal; + line-break: auto; + font-size: var(--bs-popover-font-size); + word-wrap: break-word; + background-color: var(--bs-popover-bg); + background-clip: padding-box; + border: var(--bs-popover-border-width) solid var(--bs-popover-border-color); + border-radius: var(--bs-popover-border-radius); } + .popover .popover-arrow { - display: block; - width: var(--bs-popover-arrow-width); - height: var(--bs-popover-arrow-height); + display: block; + width: var(--bs-popover-arrow-width); + height: var(--bs-popover-arrow-height); } + .popover .popover-arrow::before, .popover .popover-arrow::after { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid; - border-width: 0; + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; + border-width: 0; } .bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow { - bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } + .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { - border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; + border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } + .bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before { - bottom: 0; - border-top-color: var(--bs-popover-arrow-border); + bottom: 0; + border-top-color: var(--bs-popover-arrow-border); } + .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { - bottom: var(--bs-popover-border-width); - border-top-color: var(--bs-popover-bg); + bottom: var(--bs-popover-border-width); + border-top-color: var(--bs-popover-bg); } + .bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow { - left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); - width: var(--bs-popover-arrow-height); - height: var(--bs-popover-arrow-width); + left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + width: var(--bs-popover-arrow-height); + height: var(--bs-popover-arrow-width); } + .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { - border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; + border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } + .bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before { - left: 0; - border-right-color: var(--bs-popover-arrow-border); + left: 0; + border-right-color: var(--bs-popover-arrow-border); } + .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { - left: var(--bs-popover-border-width); - border-right-color: var(--bs-popover-bg); + left: var(--bs-popover-border-width); + border-right-color: var(--bs-popover-bg); } + .bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow { - top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } + .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { - border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); + border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } + .bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before { - top: 0; - border-bottom-color: var(--bs-popover-arrow-border); + top: 0; + border-bottom-color: var(--bs-popover-arrow-border); } + .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { - top: var(--bs-popover-border-width); - border-bottom-color: var(--bs-popover-bg); + top: var(--bs-popover-border-width); + border-bottom-color: var(--bs-popover-bg); } + .bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before { - position: absolute; - top: 0; - right: 50%; - display: block; - width: var(--bs-popover-arrow-width); - margin-right: calc(-0.5 * var(--bs-popover-arrow-width)); - content: ""; - border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg); + position: absolute; + top: 0; + right: 50%; + display: block; + width: var(--bs-popover-arrow-width); + margin-right: calc(-0.5 * var(--bs-popover-arrow-width)); + content: ""; + border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg); } + .bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow { - right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); - width: var(--bs-popover-arrow-height); - height: var(--bs-popover-arrow-width); + right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + width: var(--bs-popover-arrow-height); + height: var(--bs-popover-arrow-width); } + .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { - border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); + border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } + .bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before { - right: 0; - border-left-color: var(--bs-popover-arrow-border); + right: 0; + border-left-color: var(--bs-popover-arrow-border); } + .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { - right: var(--bs-popover-border-width); - border-left-color: var(--bs-popover-bg); + right: var(--bs-popover-border-width); + border-left-color: var(--bs-popover-bg); } + .popover-header { - padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x); - margin-bottom: 0; - font-size: var(--bs-popover-header-font-size); - color: var(--bs-popover-header-color); - background-color: var(--bs-popover-header-bg); - border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color); - border-top-right-radius: var(--bs-popover-inner-border-radius); - border-top-left-radius: var(--bs-popover-inner-border-radius); + padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x); + margin-bottom: 0; + font-size: var(--bs-popover-header-font-size); + color: var(--bs-popover-header-color); + background-color: var(--bs-popover-header-bg); + border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color); + border-top-right-radius: var(--bs-popover-inner-border-radius); + border-top-left-radius: var(--bs-popover-inner-border-radius); } + .popover-header:empty { - display: none; + display: none; } .popover-body { - padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x); - color: var(--bs-popover-body-color); + padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x); + color: var(--bs-popover-body-color); } .carousel { - position: relative; + position: relative; } .carousel.pointer-event { - touch-action: pan-y; + touch-action: pan-y; } .carousel-inner { - position: relative; - width: 100%; - overflow: hidden; + position: relative; + width: 100%; + overflow: hidden; } + .carousel-inner::after { - display: block; - clear: both; - content: ""; + display: block; + clear: both; + content: ""; } .carousel-item { - position: relative; - display: none; - float: right; - width: 100%; - margin-left: -100%; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - transition: transform 0.6s ease-in-out; + position: relative; + display: none; + float: right; + width: 100%; + margin-left: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition: transform 0.6s ease-in-out; } + @media (prefers-reduced-motion: reduce) { - .carousel-item { - transition: none; - } + .carousel-item { + transition: none; + } } .carousel-item.active, .carousel-item-next, .carousel-item-prev { - display: block; + display: block; } .carousel-item-next:not(.carousel-item-start), .active.carousel-item-end { - transform: translateX(-100%); + transform: translateX(-100%); } .carousel-item-prev:not(.carousel-item-end), .active.carousel-item-start { - transform: translateX(100%); + transform: translateX(100%); } .carousel-fade .carousel-item { - opacity: 0; - transition-property: opacity; - transform: none; + opacity: 0; + transition-property: opacity; + transform: none; } + .carousel-fade .carousel-item.active, .carousel-fade .carousel-item-next.carousel-item-start, .carousel-fade .carousel-item-prev.carousel-item-end { - z-index: 1; - opacity: 1; + z-index: 1; + opacity: 1; } + .carousel-fade .active.carousel-item-start, .carousel-fade .active.carousel-item-end { - z-index: 0; - opacity: 0; - transition: opacity 0s 0.6s; + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; } + @media (prefers-reduced-motion: reduce) { - .carousel-fade .active.carousel-item-start, - .carousel-fade .active.carousel-item-end { - transition: none; - } + .carousel-fade .active.carousel-item-start, + .carousel-fade .active.carousel-item-end { + transition: none; + } } .carousel-control-prev, .carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - z-index: 1; - display: flex; - align-items: center; - justify-content: center; - width: 15%; - padding: 0; - color: #fff; - text-align: center; - background: none; - border: 0; - opacity: 0.5; - transition: opacity 0.15s ease; + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + padding: 0; + color: #fff; + text-align: center; + background: none; + border: 0; + opacity: 0.5; + transition: opacity 0.15s ease; } + @media (prefers-reduced-motion: reduce) { - .carousel-control-prev, - .carousel-control-next { - transition: none; - } + .carousel-control-prev, + .carousel-control-next { + transition: none; + } } + .carousel-control-prev:hover, .carousel-control-prev:focus, .carousel-control-next:hover, .carousel-control-next:focus { - color: #fff; - text-decoration: none; - outline: 0; - opacity: 0.9; + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; } .carousel-control-prev { - right: 0; + right: 0; } .carousel-control-next { - left: 0; + left: 0; } .carousel-control-prev-icon, .carousel-control-next-icon { - display: inline-block; - width: 2rem; - height: 2rem; - background-repeat: no-repeat; - background-position: 50%; - background-size: 100% 100%; + display: inline-block; + width: 2rem; + height: 2rem; + background-repeat: no-repeat; + background-position: 50%; + background-size: 100% 100%; } .carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } .carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); } .carousel-indicators { - position: absolute; - left: 0; - bottom: 0; - right: 0; - z-index: 2; - display: flex; - justify-content: center; - padding: 0; - margin-left: 15%; - margin-bottom: 1rem; - margin-right: 15%; + position: absolute; + left: 0; + bottom: 0; + right: 0; + z-index: 2; + display: flex; + justify-content: center; + padding: 0; + margin-left: 15%; + margin-bottom: 1rem; + margin-right: 15%; } + .carousel-indicators [data-bs-target] { - box-sizing: content-box; - flex: 0 1 auto; - width: 30px; - height: 3px; - padding: 0; - margin-left: 3px; - margin-right: 3px; - text-indent: -999px; - cursor: pointer; - background-color: #fff; - background-clip: padding-box; - border: 0; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - opacity: 0.5; - transition: opacity 0.6s ease; + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + padding: 0; + margin-left: 3px; + margin-right: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: 0.5; + transition: opacity 0.6s ease; } + @media (prefers-reduced-motion: reduce) { - .carousel-indicators [data-bs-target] { - transition: none; - } + .carousel-indicators [data-bs-target] { + transition: none; + } } + .carousel-indicators .active { - opacity: 1; + opacity: 1; } .carousel-caption { - position: absolute; - left: 15%; - bottom: 1.25rem; - right: 15%; - padding-top: 1.25rem; - padding-bottom: 1.25rem; - color: #fff; - text-align: center; + position: absolute; + left: 15%; + bottom: 1.25rem; + right: 15%; + padding-top: 1.25rem; + padding-bottom: 1.25rem; + color: #fff; + text-align: center; } .carousel-dark .carousel-control-prev-icon, .carousel-dark .carousel-control-next-icon { - filter: invert(1) grayscale(100); + filter: invert(1) grayscale(100); } + .carousel-dark .carousel-indicators [data-bs-target] { - background-color: #000; + background-color: #000; } + .carousel-dark .carousel-caption { - color: #000; + color: #000; } [data-bs-theme=dark] .carousel .carousel-control-prev-icon, [data-bs-theme=dark] .carousel .carousel-control-next-icon, [data-bs-theme=dark].carousel .carousel-control-prev-icon, [data-bs-theme=dark].carousel .carousel-control-next-icon { - filter: invert(1) grayscale(100); + filter: invert(1) grayscale(100); } + [data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target], [data-bs-theme=dark].carousel .carousel-indicators [data-bs-target] { - background-color: #000; + background-color: #000; } + [data-bs-theme=dark] .carousel .carousel-caption, [data-bs-theme=dark].carousel .carousel-caption { - color: #000; + color: #000; } .spinner-grow, .spinner-border { - display: inline-block; - width: var(--bs-spinner-width); - height: var(--bs-spinner-height); - vertical-align: var(--bs-spinner-vertical-align); - border-radius: 50%; - animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name); + display: inline-block; + width: var(--bs-spinner-width); + height: var(--bs-spinner-height); + vertical-align: var(--bs-spinner-vertical-align); + border-radius: 50%; + animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name); } @keyframes spinner-border { - to { - transform: rotate(360deg) ; - } + to { + transform: rotate(360deg); + } } + .spinner-border { - --bs-spinner-width: 2rem; - --bs-spinner-height: 2rem; - --bs-spinner-vertical-align: -0.125em; - --bs-spinner-border-width: 0.25em; - --bs-spinner-animation-speed: 0.75s; - --bs-spinner-animation-name: spinner-border; - border: var(--bs-spinner-border-width) solid currentcolor; - border-left-color: transparent; + --bs-spinner-width: 2rem; + --bs-spinner-height: 2rem; + --bs-spinner-vertical-align: -0.125em; + --bs-spinner-border-width: 0.25em; + --bs-spinner-animation-speed: 0.75s; + --bs-spinner-animation-name: spinner-border; + border: var(--bs-spinner-border-width) solid currentcolor; + border-left-color: transparent; } .spinner-border-sm { - --bs-spinner-width: 1rem; - --bs-spinner-height: 1rem; - --bs-spinner-border-width: 0.2em; + --bs-spinner-width: 1rem; + --bs-spinner-height: 1rem; + --bs-spinner-border-width: 0.2em; } @keyframes spinner-grow { - 0% { - transform: scale(0); - } - 50% { - opacity: 1; - transform: none; - } + 0% { + transform: scale(0); + } + 50% { + opacity: 1; + transform: none; + } } + .spinner-grow { - --bs-spinner-width: 2rem; - --bs-spinner-height: 2rem; - --bs-spinner-vertical-align: -0.125em; - --bs-spinner-animation-speed: 0.75s; - --bs-spinner-animation-name: spinner-grow; - background-color: currentcolor; - opacity: 0; + --bs-spinner-width: 2rem; + --bs-spinner-height: 2rem; + --bs-spinner-vertical-align: -0.125em; + --bs-spinner-animation-speed: 0.75s; + --bs-spinner-animation-name: spinner-grow; + background-color: currentcolor; + opacity: 0; } .spinner-grow-sm { - --bs-spinner-width: 1rem; - --bs-spinner-height: 1rem; + --bs-spinner-width: 1rem; + --bs-spinner-height: 1rem; } @media (prefers-reduced-motion: reduce) { - .spinner-border, - .spinner-grow { - --bs-spinner-animation-speed: 1.5s; - } + .spinner-border, + .spinner-grow { + --bs-spinner-animation-speed: 1.5s; + } } + .offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm { - --bs-offcanvas-zindex: 1045; - --bs-offcanvas-width: 400px; - --bs-offcanvas-height: 30vh; - --bs-offcanvas-padding-x: 1rem; - --bs-offcanvas-padding-y: 1rem; - --bs-offcanvas-color: var(--bs-body-color); - --bs-offcanvas-bg: var(--bs-body-bg); - --bs-offcanvas-border-width: var(--bs-border-width); - --bs-offcanvas-border-color: var(--bs-border-color-translucent); - --bs-offcanvas-box-shadow: var(--bs-box-shadow-sm); - --bs-offcanvas-transition: transform 0.3s ease-in-out; - --bs-offcanvas-title-line-height: 1.5; + --bs-offcanvas-zindex: 1045; + --bs-offcanvas-width: 400px; + --bs-offcanvas-height: 30vh; + --bs-offcanvas-padding-x: 1rem; + --bs-offcanvas-padding-y: 1rem; + --bs-offcanvas-color: var(--bs-body-color); + --bs-offcanvas-bg: var(--bs-body-bg); + --bs-offcanvas-border-width: var(--bs-border-width); + --bs-offcanvas-border-color: var(--bs-border-color-translucent); + --bs-offcanvas-box-shadow: var(--bs-box-shadow-sm); + --bs-offcanvas-transition: transform 0.3s ease-in-out; + --bs-offcanvas-title-line-height: 1.5; +} + +@media (max-width: 575.98px) { + .offcanvas-sm { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-sm { + transition: none; + } } @media (max-width: 575.98px) { - .offcanvas-sm { + .offcanvas-sm.offcanvas-start { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-sm.offcanvas-end { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-sm.offcanvas-top { + top: 0; + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-sm.offcanvas-bottom { + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) { + transform: none; + } + + .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show { + visibility: visible; + } +} + +@media (min-width: 576px) { + .offcanvas-sm { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-sm .offcanvas-header { + display: none; + } + + .offcanvas-sm .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 767.98px) { + .offcanvas-md { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-md { + transition: none; + } +} + +@media (max-width: 767.98px) { + .offcanvas-md.offcanvas-start { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-md.offcanvas-end { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-md.offcanvas-top { + top: 0; + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-md.offcanvas-bottom { + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) { + transform: none; + } + + .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show { + visibility: visible; + } +} + +@media (min-width: 768px) { + .offcanvas-md { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-md .offcanvas-header { + display: none; + } + + .offcanvas-md .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 991.98px) { + .offcanvas-lg { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-lg { + transition: none; + } +} + +@media (max-width: 991.98px) { + .offcanvas-lg.offcanvas-start { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-lg.offcanvas-end { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-lg.offcanvas-top { + top: 0; + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-lg.offcanvas-bottom { + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) { + transform: none; + } + + .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show { + visibility: visible; + } +} + +@media (min-width: 992px) { + .offcanvas-lg { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-lg .offcanvas-header { + display: none; + } + + .offcanvas-lg .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 1199.98px) { + .offcanvas-xl { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-xl { + transition: none; + } +} + +@media (max-width: 1199.98px) { + .offcanvas-xl.offcanvas-start { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-xl.offcanvas-end { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-xl.offcanvas-top { + top: 0; + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-xl.offcanvas-bottom { + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) { + transform: none; + } + + .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show { + visibility: visible; + } +} + +@media (min-width: 1200px) { + .offcanvas-xl { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-xl .offcanvas-header { + display: none; + } + + .offcanvas-xl .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +@media (max-width: 1399.98px) { + .offcanvas-xxl { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} + +@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-xxl { + transition: none; + } +} + +@media (max-width: 1399.98px) { + .offcanvas-xxl.offcanvas-start { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } + + .offcanvas-xxl.offcanvas-end { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } + + .offcanvas-xxl.offcanvas-top { + top: 0; + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } + + .offcanvas-xxl.offcanvas-bottom { + left: 0; + right: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } + + .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) { + transform: none; + } + + .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show { + visibility: visible; + } +} + +@media (min-width: 1400px) { + .offcanvas-xxl { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + + .offcanvas-xxl .offcanvas-header { + display: none; + } + + .offcanvas-xxl .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } +} + +.offcanvas { position: fixed; bottom: 0; z-index: var(--bs-offcanvas-zindex); @@ -6299,29 +7440,31 @@ textarea.form-control-lg { background-clip: padding-box; outline: 0; transition: var(--bs-offcanvas-transition); - } } -@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-sm { - transition: none; - } + +@media (prefers-reduced-motion: reduce) { + .offcanvas { + transition: none; + } } -@media (max-width: 575.98px) { - .offcanvas-sm.offcanvas-start { + +.offcanvas.offcanvas-start { top: 0; right: 0; width: var(--bs-offcanvas-width); border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); - } - .offcanvas-sm.offcanvas-end { +} + +.offcanvas.offcanvas-end { top: 0; left: 0; width: var(--bs-offcanvas-width); border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); - } - .offcanvas-sm.offcanvas-top { +} + +.offcanvas.offcanvas-top { top: 0; left: 0; right: 0; @@ -6329,5702 +7472,6234 @@ textarea.form-control-lg { max-height: 100%; border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); - } - .offcanvas-sm.offcanvas-bottom { +} + +.offcanvas.offcanvas-bottom { left: 0; right: 0; height: var(--bs-offcanvas-height); max-height: 100%; border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); - } - .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) { +} + +.offcanvas.showing, .offcanvas.show:not(.hiding) { transform: none; - } - .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show { - visibility: visible; - } -} -@media (min-width: 576px) { - .offcanvas-sm { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-sm .offcanvas-header { - display: none; - } - .offcanvas-sm .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } -} - -@media (max-width: 767.98px) { - .offcanvas-md { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-md { - transition: none; - } -} -@media (max-width: 767.98px) { - .offcanvas-md.offcanvas-start { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-md.offcanvas-end { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-md.offcanvas-top { - top: 0; - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-md.offcanvas-bottom { - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) { - transform: none; - } - .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show { - visibility: visible; - } -} -@media (min-width: 768px) { - .offcanvas-md { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-md .offcanvas-header { - display: none; - } - .offcanvas-md .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } -} - -@media (max-width: 991.98px) { - .offcanvas-lg { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-lg { - transition: none; - } -} -@media (max-width: 991.98px) { - .offcanvas-lg.offcanvas-start { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-lg.offcanvas-end { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-lg.offcanvas-top { - top: 0; - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-lg.offcanvas-bottom { - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) { - transform: none; - } - .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show { - visibility: visible; - } -} -@media (min-width: 992px) { - .offcanvas-lg { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-lg .offcanvas-header { - display: none; - } - .offcanvas-lg .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } } -@media (max-width: 1199.98px) { - .offcanvas-xl { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-xl { - transition: none; - } -} -@media (max-width: 1199.98px) { - .offcanvas-xl.offcanvas-start { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-xl.offcanvas-end { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-xl.offcanvas-top { - top: 0; - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-xl.offcanvas-bottom { - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) { - transform: none; - } - .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show { +.offcanvas.showing, .offcanvas.hiding, .offcanvas.show { visibility: visible; - } -} -@media (min-width: 1200px) { - .offcanvas-xl { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-xl .offcanvas-header { - display: none; - } - .offcanvas-xl .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } } -@media (max-width: 1399.98px) { - .offcanvas-xxl { +.offcanvas-backdrop { position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); - } -} -@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) { - .offcanvas-xxl { - transition: none; - } -} -@media (max-width: 1399.98px) { - .offcanvas-xxl.offcanvas-start { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); - } - .offcanvas-xxl.offcanvas-end { top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); - } - .offcanvas-xxl.offcanvas-top { - top: 0; - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); - } - .offcanvas-xxl.offcanvas-bottom { - left: 0; right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); - } - .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) { - transform: none; - } - .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show { - visibility: visible; - } -} -@media (min-width: 1400px) { - .offcanvas-xxl { - --bs-offcanvas-height: auto; - --bs-offcanvas-border-width: 0; - background-color: transparent !important; - } - .offcanvas-xxl .offcanvas-header { - display: none; - } - .offcanvas-xxl .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - background-color: transparent !important; - } -} - -.offcanvas { - position: fixed; - bottom: 0; - z-index: var(--bs-offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--bs-offcanvas-color); - visibility: hidden; - background-color: var(--bs-offcanvas-bg); - background-clip: padding-box; - outline: 0; - transition: var(--bs-offcanvas-transition); -} -@media (prefers-reduced-motion: reduce) { - .offcanvas { - transition: none; - } -} -.offcanvas.offcanvas-start { - top: 0; - right: 0; - width: var(--bs-offcanvas-width); - border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(100%); -} -.offcanvas.offcanvas-end { - top: 0; - left: 0; - width: var(--bs-offcanvas-width); - border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateX(-100%); -} -.offcanvas.offcanvas-top { - top: 0; - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(-100%); -} -.offcanvas.offcanvas-bottom { - left: 0; - right: 0; - height: var(--bs-offcanvas-height); - max-height: 100%; - border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); - transform: translateY(100%); -} -.offcanvas.showing, .offcanvas.show:not(.hiding) { - transform: none; -} -.offcanvas.showing, .offcanvas.hiding, .offcanvas.show { - visibility: visible; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; } -.offcanvas-backdrop { - position: fixed; - top: 0; - right: 0; - z-index: 1040; - width: 100vw; - height: 100vh; - background-color: #000; -} .offcanvas-backdrop.fade { - opacity: 0; + opacity: 0; } + .offcanvas-backdrop.show { - opacity: 0.5; + opacity: 0.5; } .offcanvas-header { - display: flex; - align-items: center; - padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); + display: flex; + align-items: center; + padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); } + .offcanvas-header .btn-close { - padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5); - margin: calc(-0.5 * var(--bs-offcanvas-padding-y)) auto calc(-0.5 * var(--bs-offcanvas-padding-y)) calc(-0.5 * var(--bs-offcanvas-padding-x)); + padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5); + margin: calc(-0.5 * var(--bs-offcanvas-padding-y)) auto calc(-0.5 * var(--bs-offcanvas-padding-y)) calc(-0.5 * var(--bs-offcanvas-padding-x)); } .offcanvas-title { - margin-bottom: 0; - line-height: var(--bs-offcanvas-title-line-height); + margin-bottom: 0; + line-height: var(--bs-offcanvas-title-line-height); } .offcanvas-body { - flex-grow: 1; - padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); - overflow-y: auto; + flex-grow: 1; + padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); + overflow-y: auto; } .placeholder { - display: inline-block; - min-height: 1em; - vertical-align: middle; - cursor: wait; - background-color: currentcolor; - opacity: 0.5; + display: inline-block; + min-height: 1em; + vertical-align: middle; + cursor: wait; + background-color: currentcolor; + opacity: 0.5; } + .placeholder.btn::before { - display: inline-block; - content: ""; + display: inline-block; + content: ""; } .placeholder-xs { - min-height: 0.6em; + min-height: 0.6em; } .placeholder-sm { - min-height: 0.8em; + min-height: 0.8em; } .placeholder-lg { - min-height: 1.2em; + min-height: 1.2em; } .placeholder-glow .placeholder { - animation: placeholder-glow 2s ease-in-out infinite; + animation: placeholder-glow 2s ease-in-out infinite; } @keyframes placeholder-glow { - 50% { - opacity: 0.2; - } + 50% { + opacity: 0.2; + } } + .placeholder-wave { - -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); - mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); - -webkit-mask-size: 200% 100%; - mask-size: 200% 100%; - animation: placeholder-wave 2s linear infinite; + -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); + mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); + -webkit-mask-size: 200% 100%; + mask-size: 200% 100%; + animation: placeholder-wave 2s linear infinite; } @keyframes placeholder-wave { - 100% { - -webkit-mask-position: -200% 0%; - mask-position: -200% 0%; - } + 100% { + -webkit-mask-position: -200% 0%; + mask-position: -200% 0%; + } } + .clearfix::after { - display: block; - clear: both; - content: ""; + display: block; + clear: both; + content: ""; } .text-bg-primary { - color: #fff !important; - background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-secondary { - color: #fff !important; - background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-success { - color: #fff !important; - background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-info { - color: #000 !important; - background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-warning { - color: #000 !important; - background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-danger { - color: #fff !important; - background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-light { - color: #000 !important; - background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; + color: #000 !important; + background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-dark { - color: #fff !important; - background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; + color: #fff !important; + background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; } .link-primary { - color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-primary:hover, .link-primary:focus { - color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; } .link-secondary { - color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-secondary:hover, .link-secondary:focus { - color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; } .link-success { - color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-success:hover, .link-success:focus { - color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; } .link-info { - color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-info:hover, .link-info:focus { - color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; } .link-warning { - color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-warning:hover, .link-warning:focus { - color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; } .link-danger { - color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-danger:hover, .link-danger:focus { - color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; } .link-light { - color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-light:hover, .link-light:focus { - color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; } .link-dark { - color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-dark:hover, .link-dark:focus { - color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; } .link-body-emphasis { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } + .link-body-emphasis:hover, .link-body-emphasis:focus { - color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; - -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; - text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; } .focus-ring:focus { - outline: 0; - box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); + outline: 0; + box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); } .icon-link { - display: inline-flex; - gap: 0.375rem; - align-items: center; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); - text-underline-offset: 0.25em; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; + display: inline-flex; + gap: 0.375rem; + align-items: center; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-underline-offset: 0.25em; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + .icon-link > .bi { - flex-shrink: 0; - width: 1em; - height: 1em; - fill: currentcolor; - transition: 0.2s ease-in-out transform; + flex-shrink: 0; + width: 1em; + height: 1em; + fill: currentcolor; + transition: 0.2s ease-in-out transform; } + @media (prefers-reduced-motion: reduce) { - .icon-link > .bi { - transition: none; - } + .icon-link > .bi { + transition: none; + } } .icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi { - transform: var(--bs-icon-link-transform, translate3d(-0.25em, 0, 0)); + transform: var(--bs-icon-link-transform, translate3d(-0.25em, 0, 0)); } .ratio { - position: relative; - width: 100%; + position: relative; + width: 100%; } + .ratio::before { - display: block; - padding-top: var(--bs-aspect-ratio); - content: ""; + display: block; + padding-top: var(--bs-aspect-ratio); + content: ""; } + .ratio > * { - position: absolute; - top: 0; - right: 0; - width: 100%; - height: 100%; + position: absolute; + top: 0; + right: 0; + width: 100%; + height: 100%; } .ratio-1x1 { - --bs-aspect-ratio: 100%; + --bs-aspect-ratio: 100%; } .ratio-4x3 { - --bs-aspect-ratio: 75%; + --bs-aspect-ratio: 75%; } .ratio-16x9 { - --bs-aspect-ratio: 56.25%; + --bs-aspect-ratio: 56.25%; } .ratio-21x9 { - --bs-aspect-ratio: 42.8571428571%; + --bs-aspect-ratio: 42.8571428571%; } .fixed-top { - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1030; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1030; } .fixed-bottom { - position: fixed; - left: 0; - bottom: 0; - right: 0; - z-index: 1030; + position: fixed; + left: 0; + bottom: 0; + right: 0; + z-index: 1030; } .sticky-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; -} - -.sticky-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; -} - -@media (min-width: 576px) { - .sticky-sm-top { position: -webkit-sticky; position: sticky; top: 0; z-index: 1020; - } - .sticky-sm-bottom { +} + +.sticky-bottom { position: -webkit-sticky; position: sticky; bottom: 0; z-index: 1020; - } } + +@media (min-width: 576px) { + .sticky-sm-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-sm-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } +} + @media (min-width: 768px) { - .sticky-md-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-md-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-md-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-md-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 992px) { - .sticky-lg-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-lg-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-lg-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-lg-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1200px) { - .sticky-xl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + @media (min-width: 1400px) { - .sticky-xxl-top { - position: -webkit-sticky; - position: sticky; - top: 0; - z-index: 1020; - } - .sticky-xxl-bottom { - position: -webkit-sticky; - position: sticky; - bottom: 0; - z-index: 1020; - } + .sticky-xxl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } + + .sticky-xxl-bottom { + position: -webkit-sticky; + position: sticky; + bottom: 0; + z-index: 1020; + } } + .hstack { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; + display: flex; + flex-direction: row; + align-items: center; + align-self: stretch; } .vstack { - display: flex; - flex: 1 1 auto; - flex-direction: column; - align-self: stretch; + display: flex; + flex: 1 1 auto; + flex-direction: column; + align-self: stretch; } .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; } + .visually-hidden:not(caption), .visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) { - position: absolute !important; + position: absolute !important; } .stretched-link::after { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - z-index: 1; - content: ""; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 1; + content: ""; } .text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .vr { - display: inline-block; - align-self: stretch; - width: var(--bs-border-width); - min-height: 1em; - background-color: currentcolor; - opacity: 0.25; + display: inline-block; + align-self: stretch; + width: var(--bs-border-width); + min-height: 1em; + background-color: currentcolor; + opacity: 0.25; } .align-baseline { - vertical-align: baseline !important; + vertical-align: baseline !important; } .align-top { - vertical-align: top !important; + vertical-align: top !important; } .align-middle { - vertical-align: middle !important; + vertical-align: middle !important; } .align-bottom { - vertical-align: bottom !important; + vertical-align: bottom !important; } .align-text-bottom { - vertical-align: text-bottom !important; + vertical-align: text-bottom !important; } .align-text-top { - vertical-align: text-top !important; + vertical-align: text-top !important; } .float-start { - float: right !important; + float: right !important; } .float-end { - float: left !important; + float: left !important; } .float-none { - float: none !important; + float: none !important; } .object-fit-contain { - -o-object-fit: contain !important; - object-fit: contain !important; + -o-object-fit: contain !important; + object-fit: contain !important; } .object-fit-cover { - -o-object-fit: cover !important; - object-fit: cover !important; + -o-object-fit: cover !important; + object-fit: cover !important; } .object-fit-fill { - -o-object-fit: fill !important; - object-fit: fill !important; + -o-object-fit: fill !important; + object-fit: fill !important; } .object-fit-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; + -o-object-fit: scale-down !important; + object-fit: scale-down !important; } .object-fit-none { - -o-object-fit: none !important; - object-fit: none !important; + -o-object-fit: none !important; + object-fit: none !important; } .opacity-0 { - opacity: 0 !important; + opacity: 0 !important; } .opacity-25 { - opacity: 0.25 !important; + opacity: 0.25 !important; } .opacity-50 { - opacity: 0.5 !important; + opacity: 0.5 !important; } .opacity-75 { - opacity: 0.75 !important; + opacity: 0.75 !important; } .opacity-100 { - opacity: 1 !important; + opacity: 1 !important; } .overflow-auto { - overflow: auto !important; + overflow: auto !important; } .overflow-hidden { - overflow: hidden !important; + overflow: hidden !important; } .overflow-visible { - overflow: visible !important; + overflow: visible !important; } .overflow-scroll { - overflow: scroll !important; + overflow: scroll !important; } .overflow-x-auto { - overflow-x: auto !important; + overflow-x: auto !important; } .overflow-x-hidden { - overflow-x: hidden !important; + overflow-x: hidden !important; } .overflow-x-visible { - overflow-x: visible !important; + overflow-x: visible !important; } .overflow-x-scroll { - overflow-x: scroll !important; + overflow-x: scroll !important; } .overflow-y-auto { - overflow-y: auto !important; + overflow-y: auto !important; } .overflow-y-hidden { - overflow-y: hidden !important; + overflow-y: hidden !important; } .overflow-y-visible { - overflow-y: visible !important; + overflow-y: visible !important; } .overflow-y-scroll { - overflow-y: scroll !important; + overflow-y: scroll !important; } .d-inline { - display: inline !important; + display: inline !important; } .d-inline-block { - display: inline-block !important; + display: inline-block !important; } .d-block { - display: block !important; + display: block !important; } .d-grid { - display: grid !important; + display: grid !important; } .d-inline-grid { - display: inline-grid !important; + display: inline-grid !important; } .d-table { - display: table !important; + display: table !important; } .d-table-row { - display: table-row !important; + display: table-row !important; } .d-table-cell { - display: table-cell !important; + display: table-cell !important; } .d-flex { - display: flex !important; + display: flex !important; } .d-inline-flex { - display: inline-flex !important; + display: inline-flex !important; } .d-none { - display: none !important; + display: none !important; } .shadow { - box-shadow: var(--bs-box-shadow) !important; + box-shadow: var(--bs-box-shadow) !important; } .shadow-sm { - box-shadow: var(--bs-box-shadow-sm) !important; + box-shadow: var(--bs-box-shadow-sm) !important; } .shadow-lg { - box-shadow: var(--bs-box-shadow-lg) !important; + box-shadow: var(--bs-box-shadow-lg) !important; } .shadow-none { - box-shadow: none !important; + box-shadow: none !important; } .focus-ring-primary { - --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-secondary { - --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-success { - --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-info { - --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-warning { - --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-danger { - --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-light { - --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); } .focus-ring-dark { - --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); + --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); } .position-static { - position: static !important; + position: static !important; } .position-relative { - position: relative !important; + position: relative !important; } .position-absolute { - position: absolute !important; + position: absolute !important; } .position-fixed { - position: fixed !important; + position: fixed !important; } .position-sticky { - position: -webkit-sticky !important; - position: sticky !important; + position: -webkit-sticky !important; + position: sticky !important; } .top-0 { - top: 0 !important; + top: 0 !important; } .top-50 { - top: 50% !important; + top: 50% !important; } .top-100 { - top: 100% !important; + top: 100% !important; } .bottom-0 { - bottom: 0 !important; + bottom: 0 !important; } .bottom-50 { - bottom: 50% !important; + bottom: 50% !important; } .bottom-100 { - bottom: 100% !important; + bottom: 100% !important; } .start-0 { - right: 0 !important; + right: 0 !important; } .start-50 { - right: 50% !important; + right: 50% !important; } .start-100 { - right: 100% !important; + right: 100% !important; } .end-0 { - left: 0 !important; + left: 0 !important; } .end-50 { - left: 50% !important; + left: 50% !important; } .end-100 { - left: 100% !important; + left: 100% !important; } .translate-middle { - transform: translate(50%, -50%) !important; + transform: translate(50%, -50%) !important; } .translate-middle-x { - transform: translateX(50%) !important; + transform: translateX(50%) !important; } .translate-middle-y { - transform: translateY(-50%) !important; + transform: translateY(-50%) !important; } .border { - border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-0 { - border: 0 !important; + border: 0 !important; } .border-top { - border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-top-0 { - border-top: 0 !important; + border-top: 0 !important; } .border-end { - border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-end-0 { - border-left: 0 !important; + border-left: 0 !important; } .border-bottom { - border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-bottom-0 { - border-bottom: 0 !important; + border-bottom: 0 !important; } .border-start { - border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } .border-start-0 { - border-right: 0 !important; + border-right: 0 !important; } .border-primary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; } .border-secondary { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; } .border-success { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; } .border-info { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; } .border-warning { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; } .border-danger { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; } .border-light { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; } .border-dark { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } .border-black { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; } .border-white { - --bs-border-opacity: 1; - border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; + --bs-border-opacity: 1; + border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } .border-primary-subtle { - border-color: var(--bs-primary-border-subtle) !important; + border-color: var(--bs-primary-border-subtle) !important; } .border-secondary-subtle { - border-color: var(--bs-secondary-border-subtle) !important; + border-color: var(--bs-secondary-border-subtle) !important; } .border-success-subtle { - border-color: var(--bs-success-border-subtle) !important; + border-color: var(--bs-success-border-subtle) !important; } .border-info-subtle { - border-color: var(--bs-info-border-subtle) !important; + border-color: var(--bs-info-border-subtle) !important; } .border-warning-subtle { - border-color: var(--bs-warning-border-subtle) !important; + border-color: var(--bs-warning-border-subtle) !important; } .border-danger-subtle { - border-color: var(--bs-danger-border-subtle) !important; + border-color: var(--bs-danger-border-subtle) !important; } .border-light-subtle { - border-color: var(--bs-light-border-subtle) !important; + border-color: var(--bs-light-border-subtle) !important; } .border-dark-subtle { - border-color: var(--bs-dark-border-subtle) !important; + border-color: var(--bs-dark-border-subtle) !important; } .border-1 { - border-width: 1px !important; + border-width: 1px !important; } .border-2 { - border-width: 2px !important; + border-width: 2px !important; } .border-3 { - border-width: 3px !important; + border-width: 3px !important; } .border-4 { - border-width: 4px !important; + border-width: 4px !important; } .border-5 { - border-width: 5px !important; + border-width: 5px !important; } .border-opacity-10 { - --bs-border-opacity: 0.1; + --bs-border-opacity: 0.1; } .border-opacity-25 { - --bs-border-opacity: 0.25; + --bs-border-opacity: 0.25; } .border-opacity-50 { - --bs-border-opacity: 0.5; + --bs-border-opacity: 0.5; } .border-opacity-75 { - --bs-border-opacity: 0.75; + --bs-border-opacity: 0.75; } .border-opacity-100 { - --bs-border-opacity: 1; + --bs-border-opacity: 1; } .w-25 { - width: 25% !important; + width: 25% !important; } .w-50 { - width: 50% !important; + width: 50% !important; } .w-75 { - width: 75% !important; + width: 75% !important; } .w-100 { - width: 100% !important; + width: 100% !important; } .w-auto { - width: auto !important; + width: auto !important; } .mw-100 { - max-width: 100% !important; + max-width: 100% !important; } .vw-100 { - width: 100vw !important; + width: 100vw !important; } .min-vw-100 { - min-width: 100vw !important; + min-width: 100vw !important; } .h-25 { - height: 25% !important; + height: 25% !important; } .h-50 { - height: 50% !important; + height: 50% !important; } .h-75 { - height: 75% !important; + height: 75% !important; } .h-100 { - height: 100% !important; + height: 100% !important; } .h-auto { - height: auto !important; + height: auto !important; } .mh-100 { - max-height: 100% !important; + max-height: 100% !important; } .vh-100 { - height: 100vh !important; + height: 100vh !important; } .min-vh-100 { - min-height: 100vh !important; + min-height: 100vh !important; } .flex-fill { - flex: 1 1 auto !important; + flex: 1 1 auto !important; } .flex-row { - flex-direction: row !important; + flex-direction: row !important; } .flex-column { - flex-direction: column !important; + flex-direction: column !important; } .flex-row-reverse { - flex-direction: row-reverse !important; + flex-direction: row-reverse !important; } .flex-column-reverse { - flex-direction: column-reverse !important; + flex-direction: column-reverse !important; } .flex-grow-0 { - flex-grow: 0 !important; + flex-grow: 0 !important; } .flex-grow-1 { - flex-grow: 1 !important; + flex-grow: 1 !important; } .flex-shrink-0 { - flex-shrink: 0 !important; + flex-shrink: 0 !important; } .flex-shrink-1 { - flex-shrink: 1 !important; + flex-shrink: 1 !important; } .flex-wrap { - flex-wrap: wrap !important; + flex-wrap: wrap !important; } .flex-nowrap { - flex-wrap: nowrap !important; + flex-wrap: nowrap !important; } .flex-wrap-reverse { - flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; } .justify-content-start { - justify-content: flex-start !important; + justify-content: flex-start !important; } .justify-content-end { - justify-content: flex-end !important; + justify-content: flex-end !important; } .justify-content-center { - justify-content: center !important; + justify-content: center !important; } .justify-content-between { - justify-content: space-between !important; + justify-content: space-between !important; } .justify-content-around { - justify-content: space-around !important; + justify-content: space-around !important; } .justify-content-evenly { - justify-content: space-evenly !important; + justify-content: space-evenly !important; } .align-items-start { - align-items: flex-start !important; + align-items: flex-start !important; } .align-items-end { - align-items: flex-end !important; + align-items: flex-end !important; } .align-items-center { - align-items: center !important; + align-items: center !important; } .align-items-baseline { - align-items: baseline !important; + align-items: baseline !important; } .align-items-stretch { - align-items: stretch !important; + align-items: stretch !important; } .align-content-start { - align-content: flex-start !important; + align-content: flex-start !important; } .align-content-end { - align-content: flex-end !important; + align-content: flex-end !important; } .align-content-center { - align-content: center !important; + align-content: center !important; } .align-content-between { - align-content: space-between !important; + align-content: space-between !important; } .align-content-around { - align-content: space-around !important; + align-content: space-around !important; } .align-content-stretch { - align-content: stretch !important; + align-content: stretch !important; } .align-self-auto { - align-self: auto !important; + align-self: auto !important; } .align-self-start { - align-self: flex-start !important; + align-self: flex-start !important; } .align-self-end { - align-self: flex-end !important; + align-self: flex-end !important; } .align-self-center { - align-self: center !important; + align-self: center !important; } .align-self-baseline { - align-self: baseline !important; + align-self: baseline !important; } .align-self-stretch { - align-self: stretch !important; + align-self: stretch !important; } .order-first { - order: -1 !important; + order: -1 !important; } .order-0 { - order: 0 !important; + order: 0 !important; } .order-1 { - order: 1 !important; + order: 1 !important; } .order-2 { - order: 2 !important; + order: 2 !important; } .order-3 { - order: 3 !important; + order: 3 !important; } .order-4 { - order: 4 !important; + order: 4 !important; } .order-5 { - order: 5 !important; + order: 5 !important; } .order-last { - order: 6 !important; + order: 6 !important; } .m-0 { - margin: 0 !important; + margin: 0 !important; } .m-1 { - margin: 0.25rem !important; + margin: 0.25rem !important; } .m-2 { - margin: 0.5rem !important; + margin: 0.5rem !important; } .m-3 { - margin: 1rem !important; + margin: 1rem !important; } .m-4 { - margin: 1.5rem !important; + margin: 1.5rem !important; } .m-5 { - margin: 3rem !important; + margin: 3rem !important; } .m-auto { - margin: auto !important; + margin: auto !important; } .mx-0 { - margin-left: 0 !important; - margin-right: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; } .mx-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; } .mx-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; } .mx-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; + margin-left: 1rem !important; + margin-right: 1rem !important; } .mx-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; } .mx-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; + margin-left: 3rem !important; + margin-right: 3rem !important; } .mx-auto { - margin-left: auto !important; - margin-right: auto !important; + margin-left: auto !important; + margin-right: auto !important; } .my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; } .my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } .my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } .my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; + margin-top: 1rem !important; + margin-bottom: 1rem !important; } .my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } .my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; + margin-top: 3rem !important; + margin-bottom: 3rem !important; } .my-auto { - margin-top: auto !important; - margin-bottom: auto !important; + margin-top: auto !important; + margin-bottom: auto !important; } .mt-0 { - margin-top: 0 !important; + margin-top: 0 !important; } .mt-1 { - margin-top: 0.25rem !important; + margin-top: 0.25rem !important; } .mt-2 { - margin-top: 0.5rem !important; + margin-top: 0.5rem !important; } .mt-3 { - margin-top: 1rem !important; + margin-top: 1rem !important; } .mt-4 { - margin-top: 1.5rem !important; + margin-top: 1.5rem !important; } .mt-5 { - margin-top: 3rem !important; + margin-top: 3rem !important; } .mt-auto { - margin-top: auto !important; + margin-top: auto !important; } .me-0 { - margin-left: 0 !important; + margin-left: 0 !important; } .me-1 { - margin-left: 0.25rem !important; + margin-left: 0.25rem !important; } .me-2 { - margin-left: 0.5rem !important; + margin-left: 0.5rem !important; } .me-3 { - margin-left: 1rem !important; + margin-left: 1rem !important; } .me-4 { - margin-left: 1.5rem !important; + margin-left: 1.5rem !important; } .me-5 { - margin-left: 3rem !important; + margin-left: 3rem !important; } .me-auto { - margin-left: auto !important; + margin-left: auto !important; } .mb-0 { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .mb-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 0.25rem !important; } .mb-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 0.5rem !important; } .mb-3 { - margin-bottom: 1rem !important; + margin-bottom: 1rem !important; } .mb-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 1.5rem !important; } .mb-5 { - margin-bottom: 3rem !important; + margin-bottom: 3rem !important; } .mb-auto { - margin-bottom: auto !important; + margin-bottom: auto !important; } .ms-0 { - margin-right: 0 !important; + margin-right: 0 !important; } .ms-1 { - margin-right: 0.25rem !important; + margin-right: 0.25rem !important; } .ms-2 { - margin-right: 0.5rem !important; + margin-right: 0.5rem !important; } .ms-3 { - margin-right: 1rem !important; + margin-right: 1rem !important; } .ms-4 { - margin-right: 1.5rem !important; + margin-right: 1.5rem !important; } .ms-5 { - margin-right: 3rem !important; + margin-right: 3rem !important; } .ms-auto { - margin-right: auto !important; + margin-right: auto !important; } .p-0 { - padding: 0 !important; + padding: 0 !important; } .p-1 { - padding: 0.25rem !important; + padding: 0.25rem !important; } .p-2 { - padding: 0.5rem !important; + padding: 0.5rem !important; } .p-3 { - padding: 1rem !important; + padding: 1rem !important; } .p-4 { - padding: 1.5rem !important; + padding: 1.5rem !important; } .p-5 { - padding: 3rem !important; + padding: 3rem !important; } .px-0 { - padding-left: 0 !important; - padding-right: 0 !important; + padding-left: 0 !important; + padding-right: 0 !important; } .px-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; } .px-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; } .px-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; + padding-left: 1rem !important; + padding-right: 1rem !important; } .px-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; } .px-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; + padding-left: 3rem !important; + padding-right: 3rem !important; } .py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; } .py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } .py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } .py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; + padding-top: 1rem !important; + padding-bottom: 1rem !important; } .py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } .py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; + padding-top: 3rem !important; + padding-bottom: 3rem !important; } .pt-0 { - padding-top: 0 !important; + padding-top: 0 !important; } .pt-1 { - padding-top: 0.25rem !important; + padding-top: 0.25rem !important; } .pt-2 { - padding-top: 0.5rem !important; + padding-top: 0.5rem !important; } .pt-3 { - padding-top: 1rem !important; + padding-top: 1rem !important; } .pt-4 { - padding-top: 1.5rem !important; + padding-top: 1.5rem !important; } .pt-5 { - padding-top: 3rem !important; + padding-top: 3rem !important; } .pe-0 { - padding-left: 0 !important; + padding-left: 0 !important; } .pe-1 { - padding-left: 0.25rem !important; + padding-left: 0.25rem !important; } .pe-2 { - padding-left: 0.5rem !important; + padding-left: 0.5rem !important; } .pe-3 { - padding-left: 1rem !important; + padding-left: 1rem !important; } .pe-4 { - padding-left: 1.5rem !important; + padding-left: 1.5rem !important; } .pe-5 { - padding-left: 3rem !important; + padding-left: 3rem !important; } .pb-0 { - padding-bottom: 0 !important; + padding-bottom: 0 !important; } .pb-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 0.25rem !important; } .pb-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 0.5rem !important; } .pb-3 { - padding-bottom: 1rem !important; + padding-bottom: 1rem !important; } .pb-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 1.5rem !important; } .pb-5 { - padding-bottom: 3rem !important; + padding-bottom: 3rem !important; } .ps-0 { - padding-right: 0 !important; + padding-right: 0 !important; } .ps-1 { - padding-right: 0.25rem !important; + padding-right: 0.25rem !important; } .ps-2 { - padding-right: 0.5rem !important; + padding-right: 0.5rem !important; } .ps-3 { - padding-right: 1rem !important; + padding-right: 1rem !important; } .ps-4 { - padding-right: 1.5rem !important; + padding-right: 1.5rem !important; } .ps-5 { - padding-right: 3rem !important; + padding-right: 3rem !important; } .gap-0 { - gap: 0 !important; + gap: 0 !important; } .gap-1 { - gap: 0.25rem !important; + gap: 0.25rem !important; } .gap-2 { - gap: 0.5rem !important; + gap: 0.5rem !important; } .gap-3 { - gap: 1rem !important; + gap: 1rem !important; } .gap-4 { - gap: 1.5rem !important; + gap: 1.5rem !important; } .gap-5 { - gap: 3rem !important; + gap: 3rem !important; } .row-gap-0 { - row-gap: 0 !important; + row-gap: 0 !important; } .row-gap-1 { - row-gap: 0.25rem !important; + row-gap: 0.25rem !important; } .row-gap-2 { - row-gap: 0.5rem !important; + row-gap: 0.5rem !important; } .row-gap-3 { - row-gap: 1rem !important; + row-gap: 1rem !important; } .row-gap-4 { - row-gap: 1.5rem !important; + row-gap: 1.5rem !important; } .row-gap-5 { - row-gap: 3rem !important; + row-gap: 3rem !important; } .column-gap-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; + -moz-column-gap: 0 !important; + column-gap: 0 !important; } .column-gap-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; } .column-gap-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; } .column-gap-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; } .column-gap-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; } .column-gap-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; } .font-monospace { - font-family: var(--bs-font-monospace) !important; + font-family: var(--bs-font-monospace) !important; } .fs-1 { - font-size: calc(1.375rem + 1.5vw) !important; + font-size: calc(1.375rem + 1.5vw) !important; } .fs-2 { - font-size: calc(1.325rem + 0.9vw) !important; + font-size: calc(1.325rem + 0.9vw) !important; } .fs-3 { - font-size: calc(1.3rem + 0.6vw) !important; + font-size: calc(1.3rem + 0.6vw) !important; } .fs-4 { - font-size: calc(1.275rem + 0.3vw) !important; + font-size: calc(1.275rem + 0.3vw) !important; } .fs-5 { - font-size: 1.25rem !important; + font-size: 1.25rem !important; } .fs-6 { - font-size: 1rem !important; + font-size: 1rem !important; } .fst-italic { - font-style: italic !important; + font-style: italic !important; } .fst-normal { - font-style: normal !important; + font-style: normal !important; } .fw-lighter { - font-weight: lighter !important; + font-weight: lighter !important; } .fw-light { - font-weight: 300 !important; + font-weight: 300 !important; } .fw-normal { - font-weight: 400 !important; + font-weight: 400 !important; } .fw-medium { - font-weight: 500 !important; + font-weight: 500 !important; } .fw-semibold { - font-weight: 600 !important; + font-weight: 600 !important; } .fw-bold { - font-weight: 700 !important; + font-weight: 700 !important; } .fw-bolder { - font-weight: bolder !important; + font-weight: bolder !important; } .lh-1 { - line-height: 1 !important; + line-height: 1 !important; } .lh-sm { - line-height: 1.25 !important; + line-height: 1.25 !important; } .lh-base { - line-height: 1.5 !important; + line-height: 1.5 !important; } .lh-lg { - line-height: 2 !important; + line-height: 2 !important; } .text-start { - text-align: right !important; + text-align: right !important; } .text-end { - text-align: left !important; + text-align: left !important; } .text-center { - text-align: center !important; + text-align: center !important; } .text-decoration-none { - text-decoration: none !important; + text-decoration: none !important; } .text-decoration-underline { - text-decoration: underline !important; + text-decoration: underline !important; } .text-decoration-line-through { - text-decoration: line-through !important; + text-decoration: line-through !important; } .text-lowercase { - text-transform: lowercase !important; + text-transform: lowercase !important; } .text-uppercase { - text-transform: uppercase !important; + text-transform: uppercase !important; } .text-capitalize { - text-transform: capitalize !important; + text-transform: capitalize !important; } .text-wrap { - white-space: normal !important; + white-space: normal !important; } .text-nowrap { - white-space: nowrap !important; + white-space: nowrap !important; } + .text-primary { - --bs-text-opacity: 1; - color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } .text-secondary { - --bs-text-opacity: 1; - color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } .text-success { - --bs-text-opacity: 1; - color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } .text-info { - --bs-text-opacity: 1; - color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } .text-warning { - --bs-text-opacity: 1; - color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } .text-danger { - --bs-text-opacity: 1; - color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } .text-light { - --bs-text-opacity: 1; - color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } .text-dark { - --bs-text-opacity: 1; - color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } .text-black { - --bs-text-opacity: 1; - color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } .text-white { - --bs-text-opacity: 1; - color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } .text-body { - --bs-text-opacity: 1; - color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; + --bs-text-opacity: 1; + color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } .text-muted { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-black-50 { - --bs-text-opacity: 1; - color: rgba(0, 0, 0, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(0, 0, 0, 0.5) !important; } .text-white-50 { - --bs-text-opacity: 1; - color: rgba(255, 255, 255, 0.5) !important; + --bs-text-opacity: 1; + color: rgba(255, 255, 255, 0.5) !important; } .text-body-secondary { - --bs-text-opacity: 1; - color: var(--bs-secondary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } .text-body-tertiary { - --bs-text-opacity: 1; - color: var(--bs-tertiary-color) !important; + --bs-text-opacity: 1; + color: var(--bs-tertiary-color) !important; } .text-body-emphasis { - --bs-text-opacity: 1; - color: var(--bs-emphasis-color) !important; + --bs-text-opacity: 1; + color: var(--bs-emphasis-color) !important; } .text-reset { - --bs-text-opacity: 1; - color: inherit !important; + --bs-text-opacity: 1; + color: inherit !important; } .text-opacity-25 { - --bs-text-opacity: 0.25; + --bs-text-opacity: 0.25; } .text-opacity-50 { - --bs-text-opacity: 0.5; + --bs-text-opacity: 0.5; } .text-opacity-75 { - --bs-text-opacity: 0.75; + --bs-text-opacity: 0.75; } .text-opacity-100 { - --bs-text-opacity: 1; + --bs-text-opacity: 1; } .text-primary-emphasis { - color: var(--bs-primary-text-emphasis) !important; + color: var(--bs-primary-text-emphasis) !important; } .text-secondary-emphasis { - color: var(--bs-secondary-text-emphasis) !important; + color: var(--bs-secondary-text-emphasis) !important; } .text-success-emphasis { - color: var(--bs-success-text-emphasis) !important; + color: var(--bs-success-text-emphasis) !important; } .text-info-emphasis { - color: var(--bs-info-text-emphasis) !important; + color: var(--bs-info-text-emphasis) !important; } .text-warning-emphasis { - color: var(--bs-warning-text-emphasis) !important; + color: var(--bs-warning-text-emphasis) !important; } .text-danger-emphasis { - color: var(--bs-danger-text-emphasis) !important; + color: var(--bs-danger-text-emphasis) !important; } .text-light-emphasis { - color: var(--bs-light-text-emphasis) !important; + color: var(--bs-light-text-emphasis) !important; } .text-dark-emphasis { - color: var(--bs-dark-text-emphasis) !important; + color: var(--bs-dark-text-emphasis) !important; } .link-opacity-10 { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-10-hover:hover { - --bs-link-opacity: 0.1; + --bs-link-opacity: 0.1; } .link-opacity-25 { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-25-hover:hover { - --bs-link-opacity: 0.25; + --bs-link-opacity: 0.25; } .link-opacity-50 { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-50-hover:hover { - --bs-link-opacity: 0.5; + --bs-link-opacity: 0.5; } .link-opacity-75 { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-75-hover:hover { - --bs-link-opacity: 0.75; + --bs-link-opacity: 0.75; } .link-opacity-100 { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-opacity-100-hover:hover { - --bs-link-opacity: 1; + --bs-link-opacity: 1; } .link-offset-1 { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-1-hover:hover { - text-underline-offset: 0.125em !important; + text-underline-offset: 0.125em !important; } .link-offset-2 { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-2-hover:hover { - text-underline-offset: 0.25em !important; + text-underline-offset: 0.25em !important; } .link-offset-3 { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-offset-3-hover:hover { - text-underline-offset: 0.375em !important; + text-underline-offset: 0.375em !important; } .link-underline-primary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-secondary { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-success { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-info { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-warning { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-danger { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-light { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline-dark { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; - text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; } .link-underline { - --bs-link-underline-opacity: 1; - -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; - text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-underline-opacity-0 { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-0-hover:hover { - --bs-link-underline-opacity: 0; + --bs-link-underline-opacity: 0; } .link-underline-opacity-10 { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-10-hover:hover { - --bs-link-underline-opacity: 0.1; + --bs-link-underline-opacity: 0.1; } .link-underline-opacity-25 { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-25-hover:hover { - --bs-link-underline-opacity: 0.25; + --bs-link-underline-opacity: 0.25; } .link-underline-opacity-50 { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-50-hover:hover { - --bs-link-underline-opacity: 0.5; + --bs-link-underline-opacity: 0.5; } .link-underline-opacity-75 { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-75-hover:hover { - --bs-link-underline-opacity: 0.75; + --bs-link-underline-opacity: 0.75; } .link-underline-opacity-100 { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .link-underline-opacity-100-hover:hover { - --bs-link-underline-opacity: 1; + --bs-link-underline-opacity: 1; } .bg-primary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; } .bg-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; } .bg-success { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; } .bg-info { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; } .bg-warning { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; } .bg-danger { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; } .bg-light { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; } .bg-dark { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } .bg-black { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; } .bg-white { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; } .bg-body { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-transparent { - --bs-bg-opacity: 1; - background-color: transparent !important; + --bs-bg-opacity: 1; + background-color: transparent !important; } .bg-body-secondary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-body-tertiary { - --bs-bg-opacity: 1; - background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; } .bg-opacity-10 { - --bs-bg-opacity: 0.1; + --bs-bg-opacity: 0.1; } .bg-opacity-25 { - --bs-bg-opacity: 0.25; + --bs-bg-opacity: 0.25; } .bg-opacity-50 { - --bs-bg-opacity: 0.5; + --bs-bg-opacity: 0.5; } .bg-opacity-75 { - --bs-bg-opacity: 0.75; + --bs-bg-opacity: 0.75; } .bg-opacity-100 { - --bs-bg-opacity: 1; + --bs-bg-opacity: 1; } .bg-primary-subtle { - background-color: var(--bs-primary-bg-subtle) !important; + background-color: var(--bs-primary-bg-subtle) !important; } .bg-secondary-subtle { - background-color: var(--bs-secondary-bg-subtle) !important; + background-color: var(--bs-secondary-bg-subtle) !important; } .bg-success-subtle { - background-color: var(--bs-success-bg-subtle) !important; + background-color: var(--bs-success-bg-subtle) !important; } .bg-info-subtle { - background-color: var(--bs-info-bg-subtle) !important; + background-color: var(--bs-info-bg-subtle) !important; } .bg-warning-subtle { - background-color: var(--bs-warning-bg-subtle) !important; + background-color: var(--bs-warning-bg-subtle) !important; } .bg-danger-subtle { - background-color: var(--bs-danger-bg-subtle) !important; + background-color: var(--bs-danger-bg-subtle) !important; } .bg-light-subtle { - background-color: var(--bs-light-bg-subtle) !important; + background-color: var(--bs-light-bg-subtle) !important; } .bg-dark-subtle { - background-color: var(--bs-dark-bg-subtle) !important; + background-color: var(--bs-dark-bg-subtle) !important; } .bg-gradient { - background-image: var(--bs-gradient) !important; + background-image: var(--bs-gradient) !important; } .user-select-all { - -webkit-user-select: all !important; - -moz-user-select: all !important; - user-select: all !important; + -webkit-user-select: all !important; + -moz-user-select: all !important; + user-select: all !important; } .user-select-auto { - -webkit-user-select: auto !important; - -moz-user-select: auto !important; - user-select: auto !important; + -webkit-user-select: auto !important; + -moz-user-select: auto !important; + user-select: auto !important; } .user-select-none { - -webkit-user-select: none !important; - -moz-user-select: none !important; - user-select: none !important; + -webkit-user-select: none !important; + -moz-user-select: none !important; + user-select: none !important; } .pe-none { - pointer-events: none !important; + pointer-events: none !important; } .pe-auto { - pointer-events: auto !important; + pointer-events: auto !important; } .rounded { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-0 { - border-radius: 0 !important; + border-radius: 0 !important; } .rounded-1 { - border-radius: var(--bs-border-radius-sm) !important; + border-radius: var(--bs-border-radius-sm) !important; } .rounded-2 { - border-radius: var(--bs-border-radius) !important; + border-radius: var(--bs-border-radius) !important; } .rounded-3 { - border-radius: var(--bs-border-radius-lg) !important; + border-radius: var(--bs-border-radius-lg) !important; } .rounded-4 { - border-radius: var(--bs-border-radius-xl) !important; + border-radius: var(--bs-border-radius-xl) !important; } .rounded-5 { - border-radius: var(--bs-border-radius-xxl) !important; + border-radius: var(--bs-border-radius-xxl) !important; } .rounded-circle { - border-radius: 50% !important; + border-radius: 50% !important; } .rounded-pill { - border-radius: var(--bs-border-radius-pill) !important; + border-radius: var(--bs-border-radius-pill) !important; } .rounded-top { - border-top-right-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-top-0 { - border-top-right-radius: 0 !important; - border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; } .rounded-top-1 { - border-top-right-radius: var(--bs-border-radius-sm) !important; - border-top-left-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; } .rounded-top-2 { - border-top-right-radius: var(--bs-border-radius) !important; - border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; } .rounded-top-3 { - border-top-right-radius: var(--bs-border-radius-lg) !important; - border-top-left-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; } .rounded-top-4 { - border-top-right-radius: var(--bs-border-radius-xl) !important; - border-top-left-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; } .rounded-top-5 { - border-top-right-radius: var(--bs-border-radius-xxl) !important; - border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-top-circle { - border-top-right-radius: 50% !important; - border-top-left-radius: 50% !important; + border-top-right-radius: 50% !important; + border-top-left-radius: 50% !important; } .rounded-top-pill { - border-top-right-radius: var(--bs-border-radius-pill) !important; - border-top-left-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; } .rounded-end { - border-top-left-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-end-0 { - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; } .rounded-end-1 { - border-top-left-radius: var(--bs-border-radius-sm) !important; - border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; } .rounded-end-2 { - border-top-left-radius: var(--bs-border-radius) !important; - border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; } .rounded-end-3 { - border-top-left-radius: var(--bs-border-radius-lg) !important; - border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; } .rounded-end-4 { - border-top-left-radius: var(--bs-border-radius-xl) !important; - border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; } .rounded-end-5 { - border-top-left-radius: var(--bs-border-radius-xxl) !important; - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; } .rounded-end-circle { - border-top-left-radius: 50% !important; - border-bottom-left-radius: 50% !important; + border-top-left-radius: 50% !important; + border-bottom-left-radius: 50% !important; } .rounded-end-pill { - border-top-left-radius: var(--bs-border-radius-pill) !important; - border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; } .rounded-bottom { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-bottom-0 { - border-bottom-left-radius: 0 !important; - border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; } .rounded-bottom-1 { - border-bottom-left-radius: var(--bs-border-radius-sm) !important; - border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; } .rounded-bottom-2 { - border-bottom-left-radius: var(--bs-border-radius) !important; - border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; } .rounded-bottom-3 { - border-bottom-left-radius: var(--bs-border-radius-lg) !important; - border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; } .rounded-bottom-4 { - border-bottom-left-radius: var(--bs-border-radius-xl) !important; - border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; } .rounded-bottom-5 { - border-bottom-left-radius: var(--bs-border-radius-xxl) !important; - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-bottom-circle { - border-bottom-left-radius: 50% !important; - border-bottom-right-radius: 50% !important; + border-bottom-left-radius: 50% !important; + border-bottom-right-radius: 50% !important; } .rounded-bottom-pill { - border-bottom-left-radius: var(--bs-border-radius-pill) !important; - border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; } .rounded-start { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-start-0 { - border-bottom-right-radius: 0 !important; - border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; + border-top-right-radius: 0 !important; } .rounded-start-1 { - border-bottom-right-radius: var(--bs-border-radius-sm) !important; - border-top-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; } .rounded-start-2 { - border-bottom-right-radius: var(--bs-border-radius) !important; - border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; } .rounded-start-3 { - border-bottom-right-radius: var(--bs-border-radius-lg) !important; - border-top-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; } .rounded-start-4 { - border-bottom-right-radius: var(--bs-border-radius-xl) !important; - border-top-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; } .rounded-start-5 { - border-bottom-right-radius: var(--bs-border-radius-xxl) !important; - border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; } .rounded-start-circle { - border-bottom-right-radius: 50% !important; - border-top-right-radius: 50% !important; + border-bottom-right-radius: 50% !important; + border-top-right-radius: 50% !important; } .rounded-start-pill { - border-bottom-right-radius: var(--bs-border-radius-pill) !important; - border-top-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; } .visible { - visibility: visible !important; + visibility: visible !important; } .invisible { - visibility: hidden !important; + visibility: hidden !important; } .z-n1 { - z-index: -1 !important; + z-index: -1 !important; } .z-0 { - z-index: 0 !important; + z-index: 0 !important; } .z-1 { - z-index: 1 !important; + z-index: 1 !important; } .z-2 { - z-index: 2 !important; + z-index: 2 !important; } .z-3 { - z-index: 3 !important; + z-index: 3 !important; } @media (min-width: 576px) { - .float-sm-start { - float: right !important; - } - .float-sm-end { - float: left !important; - } - .float-sm-none { - float: none !important; - } - .object-fit-sm-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-sm-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-sm-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-sm-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-sm-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-sm-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-sm-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-sm-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-sm-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-sm-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-sm-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-left: 0 !important; - } - .me-sm-1 { - margin-left: 0.25rem !important; - } - .me-sm-2 { - margin-left: 0.5rem !important; - } - .me-sm-3 { - margin-left: 1rem !important; - } - .me-sm-4 { - margin-left: 1.5rem !important; - } - .me-sm-5 { - margin-left: 3rem !important; - } - .me-sm-auto { - margin-left: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-right: 0 !important; - } - .ms-sm-1 { - margin-right: 0.25rem !important; - } - .ms-sm-2 { - margin-right: 0.5rem !important; - } - .ms-sm-3 { - margin-right: 1rem !important; - } - .ms-sm-4 { - margin-right: 1.5rem !important; - } - .ms-sm-5 { - margin-right: 3rem !important; - } - .ms-sm-auto { - margin-right: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-sm-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-sm-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-sm-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-sm-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-sm-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-left: 0 !important; - } - .pe-sm-1 { - padding-left: 0.25rem !important; - } - .pe-sm-2 { - padding-left: 0.5rem !important; - } - .pe-sm-3 { - padding-left: 1rem !important; - } - .pe-sm-4 { - padding-left: 1.5rem !important; - } - .pe-sm-5 { - padding-left: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-right: 0 !important; - } - .ps-sm-1 { - padding-right: 0.25rem !important; - } - .ps-sm-2 { - padding-right: 0.5rem !important; - } - .ps-sm-3 { - padding-right: 1rem !important; - } - .ps-sm-4 { - padding-right: 1.5rem !important; - } - .ps-sm-5 { - padding-right: 3rem !important; - } - .gap-sm-0 { - gap: 0 !important; - } - .gap-sm-1 { - gap: 0.25rem !important; - } - .gap-sm-2 { - gap: 0.5rem !important; - } - .gap-sm-3 { - gap: 1rem !important; - } - .gap-sm-4 { - gap: 1.5rem !important; - } - .gap-sm-5 { - gap: 3rem !important; - } - .row-gap-sm-0 { - row-gap: 0 !important; - } - .row-gap-sm-1 { - row-gap: 0.25rem !important; - } - .row-gap-sm-2 { - row-gap: 0.5rem !important; - } - .row-gap-sm-3 { - row-gap: 1rem !important; - } - .row-gap-sm-4 { - row-gap: 1.5rem !important; - } - .row-gap-sm-5 { - row-gap: 3rem !important; - } - .column-gap-sm-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-sm-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-sm-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-sm-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-sm-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-sm-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-sm-start { - text-align: right !important; - } - .text-sm-end { - text-align: left !important; - } - .text-sm-center { - text-align: center !important; - } -} -@media (min-width: 768px) { - .float-md-start { - float: right !important; - } - .float-md-end { - float: left !important; - } - .float-md-none { - float: none !important; - } - .object-fit-md-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-md-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-md-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-md-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-md-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-md-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-md-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-md-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-md-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-md-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-md-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-left: 0 !important; - } - .me-md-1 { - margin-left: 0.25rem !important; - } - .me-md-2 { - margin-left: 0.5rem !important; - } - .me-md-3 { - margin-left: 1rem !important; - } - .me-md-4 { - margin-left: 1.5rem !important; - } - .me-md-5 { - margin-left: 3rem !important; - } - .me-md-auto { - margin-left: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-right: 0 !important; - } - .ms-md-1 { - margin-right: 0.25rem !important; - } - .ms-md-2 { - margin-right: 0.5rem !important; - } - .ms-md-3 { - margin-right: 1rem !important; - } - .ms-md-4 { - margin-right: 1.5rem !important; - } - .ms-md-5 { - margin-right: 3rem !important; - } - .ms-md-auto { - margin-right: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-md-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-md-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-md-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-md-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-md-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-left: 0 !important; - } - .pe-md-1 { - padding-left: 0.25rem !important; - } - .pe-md-2 { - padding-left: 0.5rem !important; - } - .pe-md-3 { - padding-left: 1rem !important; - } - .pe-md-4 { - padding-left: 1.5rem !important; - } - .pe-md-5 { - padding-left: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-right: 0 !important; - } - .ps-md-1 { - padding-right: 0.25rem !important; - } - .ps-md-2 { - padding-right: 0.5rem !important; - } - .ps-md-3 { - padding-right: 1rem !important; - } - .ps-md-4 { - padding-right: 1.5rem !important; - } - .ps-md-5 { - padding-right: 3rem !important; - } - .gap-md-0 { - gap: 0 !important; - } - .gap-md-1 { - gap: 0.25rem !important; - } - .gap-md-2 { - gap: 0.5rem !important; - } - .gap-md-3 { - gap: 1rem !important; - } - .gap-md-4 { - gap: 1.5rem !important; - } - .gap-md-5 { - gap: 3rem !important; - } - .row-gap-md-0 { - row-gap: 0 !important; - } - .row-gap-md-1 { - row-gap: 0.25rem !important; - } - .row-gap-md-2 { - row-gap: 0.5rem !important; - } - .row-gap-md-3 { - row-gap: 1rem !important; - } - .row-gap-md-4 { - row-gap: 1.5rem !important; - } - .row-gap-md-5 { - row-gap: 3rem !important; - } - .column-gap-md-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-md-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-md-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-md-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-md-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-md-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-md-start { - text-align: right !important; - } - .text-md-end { - text-align: left !important; - } - .text-md-center { - text-align: center !important; - } -} -@media (min-width: 992px) { - .float-lg-start { - float: right !important; - } - .float-lg-end { - float: left !important; - } - .float-lg-none { - float: none !important; - } - .object-fit-lg-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-lg-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-lg-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-lg-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-lg-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-lg-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-lg-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-lg-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-lg-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-lg-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-lg-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-left: 0 !important; - } - .me-lg-1 { - margin-left: 0.25rem !important; - } - .me-lg-2 { - margin-left: 0.5rem !important; - } - .me-lg-3 { - margin-left: 1rem !important; - } - .me-lg-4 { - margin-left: 1.5rem !important; - } - .me-lg-5 { - margin-left: 3rem !important; - } - .me-lg-auto { - margin-left: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-right: 0 !important; - } - .ms-lg-1 { - margin-right: 0.25rem !important; - } - .ms-lg-2 { - margin-right: 0.5rem !important; - } - .ms-lg-3 { - margin-right: 1rem !important; - } - .ms-lg-4 { - margin-right: 1.5rem !important; - } - .ms-lg-5 { - margin-right: 3rem !important; - } - .ms-lg-auto { - margin-right: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-lg-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-lg-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-lg-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-lg-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-lg-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-left: 0 !important; - } - .pe-lg-1 { - padding-left: 0.25rem !important; - } - .pe-lg-2 { - padding-left: 0.5rem !important; - } - .pe-lg-3 { - padding-left: 1rem !important; - } - .pe-lg-4 { - padding-left: 1.5rem !important; - } - .pe-lg-5 { - padding-left: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-right: 0 !important; - } - .ps-lg-1 { - padding-right: 0.25rem !important; - } - .ps-lg-2 { - padding-right: 0.5rem !important; - } - .ps-lg-3 { - padding-right: 1rem !important; - } - .ps-lg-4 { - padding-right: 1.5rem !important; - } - .ps-lg-5 { - padding-right: 3rem !important; - } - .gap-lg-0 { - gap: 0 !important; - } - .gap-lg-1 { - gap: 0.25rem !important; - } - .gap-lg-2 { - gap: 0.5rem !important; - } - .gap-lg-3 { - gap: 1rem !important; - } - .gap-lg-4 { - gap: 1.5rem !important; - } - .gap-lg-5 { - gap: 3rem !important; - } - .row-gap-lg-0 { - row-gap: 0 !important; - } - .row-gap-lg-1 { - row-gap: 0.25rem !important; - } - .row-gap-lg-2 { - row-gap: 0.5rem !important; - } - .row-gap-lg-3 { - row-gap: 1rem !important; - } - .row-gap-lg-4 { - row-gap: 1.5rem !important; - } - .row-gap-lg-5 { - row-gap: 3rem !important; - } - .column-gap-lg-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-lg-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-lg-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-lg-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-lg-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-lg-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-lg-start { - text-align: right !important; - } - .text-lg-end { - text-align: left !important; - } - .text-lg-center { - text-align: center !important; - } -} -@media (min-width: 1200px) { - .float-xl-start { - float: right !important; - } - .float-xl-end { - float: left !important; - } - .float-xl-none { - float: none !important; - } - .object-fit-xl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-left: 0 !important; - } - .me-xl-1 { - margin-left: 0.25rem !important; - } - .me-xl-2 { - margin-left: 0.5rem !important; - } - .me-xl-3 { - margin-left: 1rem !important; - } - .me-xl-4 { - margin-left: 1.5rem !important; - } - .me-xl-5 { - margin-left: 3rem !important; - } - .me-xl-auto { - margin-left: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-right: 0 !important; - } - .ms-xl-1 { - margin-right: 0.25rem !important; - } - .ms-xl-2 { - margin-right: 0.5rem !important; - } - .ms-xl-3 { - margin-right: 1rem !important; - } - .ms-xl-4 { - margin-right: 1.5rem !important; - } - .ms-xl-5 { - margin-right: 3rem !important; - } - .ms-xl-auto { - margin-right: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-left: 0 !important; - } - .pe-xl-1 { - padding-left: 0.25rem !important; - } - .pe-xl-2 { - padding-left: 0.5rem !important; - } - .pe-xl-3 { - padding-left: 1rem !important; - } - .pe-xl-4 { - padding-left: 1.5rem !important; - } - .pe-xl-5 { - padding-left: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-right: 0 !important; - } - .ps-xl-1 { - padding-right: 0.25rem !important; - } - .ps-xl-2 { - padding-right: 0.5rem !important; - } - .ps-xl-3 { - padding-right: 1rem !important; - } - .ps-xl-4 { - padding-right: 1.5rem !important; - } - .ps-xl-5 { - padding-right: 3rem !important; - } - .gap-xl-0 { - gap: 0 !important; - } - .gap-xl-1 { - gap: 0.25rem !important; - } - .gap-xl-2 { - gap: 0.5rem !important; - } - .gap-xl-3 { - gap: 1rem !important; - } - .gap-xl-4 { - gap: 1.5rem !important; - } - .gap-xl-5 { - gap: 3rem !important; - } - .row-gap-xl-0 { - row-gap: 0 !important; - } - .row-gap-xl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xl-3 { - row-gap: 1rem !important; - } - .row-gap-xl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xl-5 { - row-gap: 3rem !important; - } - .column-gap-xl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xl-start { - text-align: right !important; - } - .text-xl-end { - text-align: left !important; - } - .text-xl-center { - text-align: center !important; - } -} -@media (min-width: 1400px) { - .float-xxl-start { - float: right !important; - } - .float-xxl-end { - float: left !important; - } - .float-xxl-none { - float: none !important; - } - .object-fit-xxl-contain { - -o-object-fit: contain !important; - object-fit: contain !important; - } - .object-fit-xxl-cover { - -o-object-fit: cover !important; - object-fit: cover !important; - } - .object-fit-xxl-fill { - -o-object-fit: fill !important; - object-fit: fill !important; - } - .object-fit-xxl-scale { - -o-object-fit: scale-down !important; - object-fit: scale-down !important; - } - .object-fit-xxl-none { - -o-object-fit: none !important; - object-fit: none !important; - } - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xxl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xxl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xxl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xxl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xxl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xxl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-left: 0 !important; - } - .me-xxl-1 { - margin-left: 0.25rem !important; - } - .me-xxl-2 { - margin-left: 0.5rem !important; - } - .me-xxl-3 { - margin-left: 1rem !important; - } - .me-xxl-4 { - margin-left: 1.5rem !important; - } - .me-xxl-5 { - margin-left: 3rem !important; - } - .me-xxl-auto { - margin-left: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-right: 0 !important; - } - .ms-xxl-1 { - margin-right: 0.25rem !important; - } - .ms-xxl-2 { - margin-right: 0.5rem !important; - } - .ms-xxl-3 { - margin-right: 1rem !important; - } - .ms-xxl-4 { - margin-right: 1.5rem !important; - } - .ms-xxl-5 { - margin-right: 3rem !important; - } - .ms-xxl-auto { - margin-right: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xxl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xxl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xxl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xxl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xxl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-left: 0 !important; - } - .pe-xxl-1 { - padding-left: 0.25rem !important; - } - .pe-xxl-2 { - padding-left: 0.5rem !important; - } - .pe-xxl-3 { - padding-left: 1rem !important; - } - .pe-xxl-4 { - padding-left: 1.5rem !important; - } - .pe-xxl-5 { - padding-left: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-right: 0 !important; - } - .ps-xxl-1 { - padding-right: 0.25rem !important; - } - .ps-xxl-2 { - padding-right: 0.5rem !important; - } - .ps-xxl-3 { - padding-right: 1rem !important; - } - .ps-xxl-4 { - padding-right: 1.5rem !important; - } - .ps-xxl-5 { - padding-right: 3rem !important; - } - .gap-xxl-0 { - gap: 0 !important; - } - .gap-xxl-1 { - gap: 0.25rem !important; - } - .gap-xxl-2 { - gap: 0.5rem !important; - } - .gap-xxl-3 { - gap: 1rem !important; - } - .gap-xxl-4 { - gap: 1.5rem !important; - } - .gap-xxl-5 { - gap: 3rem !important; - } - .row-gap-xxl-0 { - row-gap: 0 !important; - } - .row-gap-xxl-1 { - row-gap: 0.25rem !important; - } - .row-gap-xxl-2 { - row-gap: 0.5rem !important; - } - .row-gap-xxl-3 { - row-gap: 1rem !important; - } - .row-gap-xxl-4 { - row-gap: 1.5rem !important; - } - .row-gap-xxl-5 { - row-gap: 3rem !important; - } - .column-gap-xxl-0 { - -moz-column-gap: 0 !important; - column-gap: 0 !important; - } - .column-gap-xxl-1 { - -moz-column-gap: 0.25rem !important; - column-gap: 0.25rem !important; - } - .column-gap-xxl-2 { - -moz-column-gap: 0.5rem !important; - column-gap: 0.5rem !important; - } - .column-gap-xxl-3 { - -moz-column-gap: 1rem !important; - column-gap: 1rem !important; - } - .column-gap-xxl-4 { - -moz-column-gap: 1.5rem !important; - column-gap: 1.5rem !important; - } - .column-gap-xxl-5 { - -moz-column-gap: 3rem !important; - column-gap: 3rem !important; - } - .text-xxl-start { - text-align: right !important; - } - .text-xxl-end { - text-align: left !important; - } - .text-xxl-center { - text-align: center !important; - } + .float-sm-start { + float: right !important; + } + + .float-sm-end { + float: left !important; + } + + .float-sm-none { + float: none !important; + } + + .object-fit-sm-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-sm-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-sm-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-sm-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-sm-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-grid { + display: grid !important; + } + + .d-sm-inline-grid { + display: inline-grid !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: flex !important; + } + + .d-sm-inline-flex { + display: inline-flex !important; + } + + .d-sm-none { + display: none !important; + } + + .flex-sm-fill { + flex: 1 1 auto !important; + } + + .flex-sm-row { + flex-direction: row !important; + } + + .flex-sm-column { + flex-direction: column !important; + } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-sm-wrap { + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-sm-start { + justify-content: flex-start !important; + } + + .justify-content-sm-end { + justify-content: flex-end !important; + } + + .justify-content-sm-center { + justify-content: center !important; + } + + .justify-content-sm-between { + justify-content: space-between !important; + } + + .justify-content-sm-around { + justify-content: space-around !important; + } + + .justify-content-sm-evenly { + justify-content: space-evenly !important; + } + + .align-items-sm-start { + align-items: flex-start !important; + } + + .align-items-sm-end { + align-items: flex-end !important; + } + + .align-items-sm-center { + align-items: center !important; + } + + .align-items-sm-baseline { + align-items: baseline !important; + } + + .align-items-sm-stretch { + align-items: stretch !important; + } + + .align-content-sm-start { + align-content: flex-start !important; + } + + .align-content-sm-end { + align-content: flex-end !important; + } + + .align-content-sm-center { + align-content: center !important; + } + + .align-content-sm-between { + align-content: space-between !important; + } + + .align-content-sm-around { + align-content: space-around !important; + } + + .align-content-sm-stretch { + align-content: stretch !important; + } + + .align-self-sm-auto { + align-self: auto !important; + } + + .align-self-sm-start { + align-self: flex-start !important; + } + + .align-self-sm-end { + align-self: flex-end !important; + } + + .align-self-sm-center { + align-self: center !important; + } + + .align-self-sm-baseline { + align-self: baseline !important; + } + + .align-self-sm-stretch { + align-self: stretch !important; + } + + .order-sm-first { + order: -1 !important; + } + + .order-sm-0 { + order: 0 !important; + } + + .order-sm-1 { + order: 1 !important; + } + + .order-sm-2 { + order: 2 !important; + } + + .order-sm-3 { + order: 3 !important; + } + + .order-sm-4 { + order: 4 !important; + } + + .order-sm-5 { + order: 5 !important; + } + + .order-sm-last { + order: 6 !important; + } + + .m-sm-0 { + margin: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mx-sm-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-sm-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-sm-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-sm-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-sm-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-sm-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-sm-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-sm-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-sm-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-sm-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-sm-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-sm-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-sm-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-sm-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-sm-0 { + margin-top: 0 !important; + } + + .mt-sm-1 { + margin-top: 0.25rem !important; + } + + .mt-sm-2 { + margin-top: 0.5rem !important; + } + + .mt-sm-3 { + margin-top: 1rem !important; + } + + .mt-sm-4 { + margin-top: 1.5rem !important; + } + + .mt-sm-5 { + margin-top: 3rem !important; + } + + .mt-sm-auto { + margin-top: auto !important; + } + + .me-sm-0 { + margin-left: 0 !important; + } + + .me-sm-1 { + margin-left: 0.25rem !important; + } + + .me-sm-2 { + margin-left: 0.5rem !important; + } + + .me-sm-3 { + margin-left: 1rem !important; + } + + .me-sm-4 { + margin-left: 1.5rem !important; + } + + .me-sm-5 { + margin-left: 3rem !important; + } + + .me-sm-auto { + margin-left: auto !important; + } + + .mb-sm-0 { + margin-bottom: 0 !important; + } + + .mb-sm-1 { + margin-bottom: 0.25rem !important; + } + + .mb-sm-2 { + margin-bottom: 0.5rem !important; + } + + .mb-sm-3 { + margin-bottom: 1rem !important; + } + + .mb-sm-4 { + margin-bottom: 1.5rem !important; + } + + .mb-sm-5 { + margin-bottom: 3rem !important; + } + + .mb-sm-auto { + margin-bottom: auto !important; + } + + .ms-sm-0 { + margin-right: 0 !important; + } + + .ms-sm-1 { + margin-right: 0.25rem !important; + } + + .ms-sm-2 { + margin-right: 0.5rem !important; + } + + .ms-sm-3 { + margin-right: 1rem !important; + } + + .ms-sm-4 { + margin-right: 1.5rem !important; + } + + .ms-sm-5 { + margin-right: 3rem !important; + } + + .ms-sm-auto { + margin-right: auto !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .px-sm-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-sm-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-sm-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-sm-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-sm-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-sm-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-sm-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-sm-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-sm-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-sm-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-sm-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-sm-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-sm-0 { + padding-top: 0 !important; + } + + .pt-sm-1 { + padding-top: 0.25rem !important; + } + + .pt-sm-2 { + padding-top: 0.5rem !important; + } + + .pt-sm-3 { + padding-top: 1rem !important; + } + + .pt-sm-4 { + padding-top: 1.5rem !important; + } + + .pt-sm-5 { + padding-top: 3rem !important; + } + + .pe-sm-0 { + padding-left: 0 !important; + } + + .pe-sm-1 { + padding-left: 0.25rem !important; + } + + .pe-sm-2 { + padding-left: 0.5rem !important; + } + + .pe-sm-3 { + padding-left: 1rem !important; + } + + .pe-sm-4 { + padding-left: 1.5rem !important; + } + + .pe-sm-5 { + padding-left: 3rem !important; + } + + .pb-sm-0 { + padding-bottom: 0 !important; + } + + .pb-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pb-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pb-sm-3 { + padding-bottom: 1rem !important; + } + + .pb-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pb-sm-5 { + padding-bottom: 3rem !important; + } + + .ps-sm-0 { + padding-right: 0 !important; + } + + .ps-sm-1 { + padding-right: 0.25rem !important; + } + + .ps-sm-2 { + padding-right: 0.5rem !important; + } + + .ps-sm-3 { + padding-right: 1rem !important; + } + + .ps-sm-4 { + padding-right: 1.5rem !important; + } + + .ps-sm-5 { + padding-right: 3rem !important; + } + + .gap-sm-0 { + gap: 0 !important; + } + + .gap-sm-1 { + gap: 0.25rem !important; + } + + .gap-sm-2 { + gap: 0.5rem !important; + } + + .gap-sm-3 { + gap: 1rem !important; + } + + .gap-sm-4 { + gap: 1.5rem !important; + } + + .gap-sm-5 { + gap: 3rem !important; + } + + .row-gap-sm-0 { + row-gap: 0 !important; + } + + .row-gap-sm-1 { + row-gap: 0.25rem !important; + } + + .row-gap-sm-2 { + row-gap: 0.5rem !important; + } + + .row-gap-sm-3 { + row-gap: 1rem !important; + } + + .row-gap-sm-4 { + row-gap: 1.5rem !important; + } + + .row-gap-sm-5 { + row-gap: 3rem !important; + } + + .column-gap-sm-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-sm-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-sm-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-sm-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-sm-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-sm-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-sm-start { + text-align: right !important; + } + + .text-sm-end { + text-align: left !important; + } + + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .float-md-start { + float: right !important; + } + + .float-md-end { + float: left !important; + } + + .float-md-none { + float: none !important; + } + + .object-fit-md-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-md-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-md-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-md-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-md-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-grid { + display: grid !important; + } + + .d-md-inline-grid { + display: inline-grid !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: flex !important; + } + + .d-md-inline-flex { + display: inline-flex !important; + } + + .d-md-none { + display: none !important; + } + + .flex-md-fill { + flex: 1 1 auto !important; + } + + .flex-md-row { + flex-direction: row !important; + } + + .flex-md-column { + flex-direction: column !important; + } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-md-grow-0 { + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-md-wrap { + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-md-start { + justify-content: flex-start !important; + } + + .justify-content-md-end { + justify-content: flex-end !important; + } + + .justify-content-md-center { + justify-content: center !important; + } + + .justify-content-md-between { + justify-content: space-between !important; + } + + .justify-content-md-around { + justify-content: space-around !important; + } + + .justify-content-md-evenly { + justify-content: space-evenly !important; + } + + .align-items-md-start { + align-items: flex-start !important; + } + + .align-items-md-end { + align-items: flex-end !important; + } + + .align-items-md-center { + align-items: center !important; + } + + .align-items-md-baseline { + align-items: baseline !important; + } + + .align-items-md-stretch { + align-items: stretch !important; + } + + .align-content-md-start { + align-content: flex-start !important; + } + + .align-content-md-end { + align-content: flex-end !important; + } + + .align-content-md-center { + align-content: center !important; + } + + .align-content-md-between { + align-content: space-between !important; + } + + .align-content-md-around { + align-content: space-around !important; + } + + .align-content-md-stretch { + align-content: stretch !important; + } + + .align-self-md-auto { + align-self: auto !important; + } + + .align-self-md-start { + align-self: flex-start !important; + } + + .align-self-md-end { + align-self: flex-end !important; + } + + .align-self-md-center { + align-self: center !important; + } + + .align-self-md-baseline { + align-self: baseline !important; + } + + .align-self-md-stretch { + align-self: stretch !important; + } + + .order-md-first { + order: -1 !important; + } + + .order-md-0 { + order: 0 !important; + } + + .order-md-1 { + order: 1 !important; + } + + .order-md-2 { + order: 2 !important; + } + + .order-md-3 { + order: 3 !important; + } + + .order-md-4 { + order: 4 !important; + } + + .order-md-5 { + order: 5 !important; + } + + .order-md-last { + order: 6 !important; + } + + .m-md-0 { + margin: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mx-md-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-md-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-md-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-md-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-md-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-md-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-md-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-md-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-md-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-md-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-md-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-md-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-md-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-md-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-md-0 { + margin-top: 0 !important; + } + + .mt-md-1 { + margin-top: 0.25rem !important; + } + + .mt-md-2 { + margin-top: 0.5rem !important; + } + + .mt-md-3 { + margin-top: 1rem !important; + } + + .mt-md-4 { + margin-top: 1.5rem !important; + } + + .mt-md-5 { + margin-top: 3rem !important; + } + + .mt-md-auto { + margin-top: auto !important; + } + + .me-md-0 { + margin-left: 0 !important; + } + + .me-md-1 { + margin-left: 0.25rem !important; + } + + .me-md-2 { + margin-left: 0.5rem !important; + } + + .me-md-3 { + margin-left: 1rem !important; + } + + .me-md-4 { + margin-left: 1.5rem !important; + } + + .me-md-5 { + margin-left: 3rem !important; + } + + .me-md-auto { + margin-left: auto !important; + } + + .mb-md-0 { + margin-bottom: 0 !important; + } + + .mb-md-1 { + margin-bottom: 0.25rem !important; + } + + .mb-md-2 { + margin-bottom: 0.5rem !important; + } + + .mb-md-3 { + margin-bottom: 1rem !important; + } + + .mb-md-4 { + margin-bottom: 1.5rem !important; + } + + .mb-md-5 { + margin-bottom: 3rem !important; + } + + .mb-md-auto { + margin-bottom: auto !important; + } + + .ms-md-0 { + margin-right: 0 !important; + } + + .ms-md-1 { + margin-right: 0.25rem !important; + } + + .ms-md-2 { + margin-right: 0.5rem !important; + } + + .ms-md-3 { + margin-right: 1rem !important; + } + + .ms-md-4 { + margin-right: 1.5rem !important; + } + + .ms-md-5 { + margin-right: 3rem !important; + } + + .ms-md-auto { + margin-right: auto !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .px-md-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-md-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-md-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-md-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-md-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-md-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-md-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-md-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-md-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-md-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-md-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-md-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-md-0 { + padding-top: 0 !important; + } + + .pt-md-1 { + padding-top: 0.25rem !important; + } + + .pt-md-2 { + padding-top: 0.5rem !important; + } + + .pt-md-3 { + padding-top: 1rem !important; + } + + .pt-md-4 { + padding-top: 1.5rem !important; + } + + .pt-md-5 { + padding-top: 3rem !important; + } + + .pe-md-0 { + padding-left: 0 !important; + } + + .pe-md-1 { + padding-left: 0.25rem !important; + } + + .pe-md-2 { + padding-left: 0.5rem !important; + } + + .pe-md-3 { + padding-left: 1rem !important; + } + + .pe-md-4 { + padding-left: 1.5rem !important; + } + + .pe-md-5 { + padding-left: 3rem !important; + } + + .pb-md-0 { + padding-bottom: 0 !important; + } + + .pb-md-1 { + padding-bottom: 0.25rem !important; + } + + .pb-md-2 { + padding-bottom: 0.5rem !important; + } + + .pb-md-3 { + padding-bottom: 1rem !important; + } + + .pb-md-4 { + padding-bottom: 1.5rem !important; + } + + .pb-md-5 { + padding-bottom: 3rem !important; + } + + .ps-md-0 { + padding-right: 0 !important; + } + + .ps-md-1 { + padding-right: 0.25rem !important; + } + + .ps-md-2 { + padding-right: 0.5rem !important; + } + + .ps-md-3 { + padding-right: 1rem !important; + } + + .ps-md-4 { + padding-right: 1.5rem !important; + } + + .ps-md-5 { + padding-right: 3rem !important; + } + + .gap-md-0 { + gap: 0 !important; + } + + .gap-md-1 { + gap: 0.25rem !important; + } + + .gap-md-2 { + gap: 0.5rem !important; + } + + .gap-md-3 { + gap: 1rem !important; + } + + .gap-md-4 { + gap: 1.5rem !important; + } + + .gap-md-5 { + gap: 3rem !important; + } + + .row-gap-md-0 { + row-gap: 0 !important; + } + + .row-gap-md-1 { + row-gap: 0.25rem !important; + } + + .row-gap-md-2 { + row-gap: 0.5rem !important; + } + + .row-gap-md-3 { + row-gap: 1rem !important; + } + + .row-gap-md-4 { + row-gap: 1.5rem !important; + } + + .row-gap-md-5 { + row-gap: 3rem !important; + } + + .column-gap-md-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-md-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-md-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-md-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-md-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-md-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-md-start { + text-align: right !important; + } + + .text-md-end { + text-align: left !important; + } + + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .float-lg-start { + float: right !important; + } + + .float-lg-end { + float: left !important; + } + + .float-lg-none { + float: none !important; + } + + .object-fit-lg-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-lg-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-lg-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-lg-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-lg-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-grid { + display: grid !important; + } + + .d-lg-inline-grid { + display: inline-grid !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: flex !important; + } + + .d-lg-inline-flex { + display: inline-flex !important; + } + + .d-lg-none { + display: none !important; + } + + .flex-lg-fill { + flex: 1 1 auto !important; + } + + .flex-lg-row { + flex-direction: row !important; + } + + .flex-lg-column { + flex-direction: column !important; + } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-lg-wrap { + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-lg-start { + justify-content: flex-start !important; + } + + .justify-content-lg-end { + justify-content: flex-end !important; + } + + .justify-content-lg-center { + justify-content: center !important; + } + + .justify-content-lg-between { + justify-content: space-between !important; + } + + .justify-content-lg-around { + justify-content: space-around !important; + } + + .justify-content-lg-evenly { + justify-content: space-evenly !important; + } + + .align-items-lg-start { + align-items: flex-start !important; + } + + .align-items-lg-end { + align-items: flex-end !important; + } + + .align-items-lg-center { + align-items: center !important; + } + + .align-items-lg-baseline { + align-items: baseline !important; + } + + .align-items-lg-stretch { + align-items: stretch !important; + } + + .align-content-lg-start { + align-content: flex-start !important; + } + + .align-content-lg-end { + align-content: flex-end !important; + } + + .align-content-lg-center { + align-content: center !important; + } + + .align-content-lg-between { + align-content: space-between !important; + } + + .align-content-lg-around { + align-content: space-around !important; + } + + .align-content-lg-stretch { + align-content: stretch !important; + } + + .align-self-lg-auto { + align-self: auto !important; + } + + .align-self-lg-start { + align-self: flex-start !important; + } + + .align-self-lg-end { + align-self: flex-end !important; + } + + .align-self-lg-center { + align-self: center !important; + } + + .align-self-lg-baseline { + align-self: baseline !important; + } + + .align-self-lg-stretch { + align-self: stretch !important; + } + + .order-lg-first { + order: -1 !important; + } + + .order-lg-0 { + order: 0 !important; + } + + .order-lg-1 { + order: 1 !important; + } + + .order-lg-2 { + order: 2 !important; + } + + .order-lg-3 { + order: 3 !important; + } + + .order-lg-4 { + order: 4 !important; + } + + .order-lg-5 { + order: 5 !important; + } + + .order-lg-last { + order: 6 !important; + } + + .m-lg-0 { + margin: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mx-lg-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-lg-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-lg-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-lg-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-lg-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-lg-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-lg-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-lg-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-lg-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-lg-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-lg-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-lg-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-lg-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-lg-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-lg-0 { + margin-top: 0 !important; + } + + .mt-lg-1 { + margin-top: 0.25rem !important; + } + + .mt-lg-2 { + margin-top: 0.5rem !important; + } + + .mt-lg-3 { + margin-top: 1rem !important; + } + + .mt-lg-4 { + margin-top: 1.5rem !important; + } + + .mt-lg-5 { + margin-top: 3rem !important; + } + + .mt-lg-auto { + margin-top: auto !important; + } + + .me-lg-0 { + margin-left: 0 !important; + } + + .me-lg-1 { + margin-left: 0.25rem !important; + } + + .me-lg-2 { + margin-left: 0.5rem !important; + } + + .me-lg-3 { + margin-left: 1rem !important; + } + + .me-lg-4 { + margin-left: 1.5rem !important; + } + + .me-lg-5 { + margin-left: 3rem !important; + } + + .me-lg-auto { + margin-left: auto !important; + } + + .mb-lg-0 { + margin-bottom: 0 !important; + } + + .mb-lg-1 { + margin-bottom: 0.25rem !important; + } + + .mb-lg-2 { + margin-bottom: 0.5rem !important; + } + + .mb-lg-3 { + margin-bottom: 1rem !important; + } + + .mb-lg-4 { + margin-bottom: 1.5rem !important; + } + + .mb-lg-5 { + margin-bottom: 3rem !important; + } + + .mb-lg-auto { + margin-bottom: auto !important; + } + + .ms-lg-0 { + margin-right: 0 !important; + } + + .ms-lg-1 { + margin-right: 0.25rem !important; + } + + .ms-lg-2 { + margin-right: 0.5rem !important; + } + + .ms-lg-3 { + margin-right: 1rem !important; + } + + .ms-lg-4 { + margin-right: 1.5rem !important; + } + + .ms-lg-5 { + margin-right: 3rem !important; + } + + .ms-lg-auto { + margin-right: auto !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .px-lg-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-lg-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-lg-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-lg-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-lg-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-lg-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-lg-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-lg-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-lg-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-lg-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-lg-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-lg-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-lg-0 { + padding-top: 0 !important; + } + + .pt-lg-1 { + padding-top: 0.25rem !important; + } + + .pt-lg-2 { + padding-top: 0.5rem !important; + } + + .pt-lg-3 { + padding-top: 1rem !important; + } + + .pt-lg-4 { + padding-top: 1.5rem !important; + } + + .pt-lg-5 { + padding-top: 3rem !important; + } + + .pe-lg-0 { + padding-left: 0 !important; + } + + .pe-lg-1 { + padding-left: 0.25rem !important; + } + + .pe-lg-2 { + padding-left: 0.5rem !important; + } + + .pe-lg-3 { + padding-left: 1rem !important; + } + + .pe-lg-4 { + padding-left: 1.5rem !important; + } + + .pe-lg-5 { + padding-left: 3rem !important; + } + + .pb-lg-0 { + padding-bottom: 0 !important; + } + + .pb-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pb-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pb-lg-3 { + padding-bottom: 1rem !important; + } + + .pb-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pb-lg-5 { + padding-bottom: 3rem !important; + } + + .ps-lg-0 { + padding-right: 0 !important; + } + + .ps-lg-1 { + padding-right: 0.25rem !important; + } + + .ps-lg-2 { + padding-right: 0.5rem !important; + } + + .ps-lg-3 { + padding-right: 1rem !important; + } + + .ps-lg-4 { + padding-right: 1.5rem !important; + } + + .ps-lg-5 { + padding-right: 3rem !important; + } + + .gap-lg-0 { + gap: 0 !important; + } + + .gap-lg-1 { + gap: 0.25rem !important; + } + + .gap-lg-2 { + gap: 0.5rem !important; + } + + .gap-lg-3 { + gap: 1rem !important; + } + + .gap-lg-4 { + gap: 1.5rem !important; + } + + .gap-lg-5 { + gap: 3rem !important; + } + + .row-gap-lg-0 { + row-gap: 0 !important; + } + + .row-gap-lg-1 { + row-gap: 0.25rem !important; + } + + .row-gap-lg-2 { + row-gap: 0.5rem !important; + } + + .row-gap-lg-3 { + row-gap: 1rem !important; + } + + .row-gap-lg-4 { + row-gap: 1.5rem !important; + } + + .row-gap-lg-5 { + row-gap: 3rem !important; + } + + .column-gap-lg-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-lg-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-lg-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-lg-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-lg-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-lg-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-lg-start { + text-align: right !important; + } + + .text-lg-end { + text-align: left !important; + } + + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .float-xl-start { + float: right !important; + } + + .float-xl-end { + float: left !important; + } + + .float-xl-none { + float: none !important; + } + + .object-fit-xl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-grid { + display: grid !important; + } + + .d-xl-inline-grid { + display: inline-grid !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: flex !important; + } + + .d-xl-inline-flex { + display: inline-flex !important; + } + + .d-xl-none { + display: none !important; + } + + .flex-xl-fill { + flex: 1 1 auto !important; + } + + .flex-xl-row { + flex-direction: row !important; + } + + .flex-xl-column { + flex-direction: column !important; + } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xl-wrap { + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xl-start { + justify-content: flex-start !important; + } + + .justify-content-xl-end { + justify-content: flex-end !important; + } + + .justify-content-xl-center { + justify-content: center !important; + } + + .justify-content-xl-between { + justify-content: space-between !important; + } + + .justify-content-xl-around { + justify-content: space-around !important; + } + + .justify-content-xl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xl-start { + align-items: flex-start !important; + } + + .align-items-xl-end { + align-items: flex-end !important; + } + + .align-items-xl-center { + align-items: center !important; + } + + .align-items-xl-baseline { + align-items: baseline !important; + } + + .align-items-xl-stretch { + align-items: stretch !important; + } + + .align-content-xl-start { + align-content: flex-start !important; + } + + .align-content-xl-end { + align-content: flex-end !important; + } + + .align-content-xl-center { + align-content: center !important; + } + + .align-content-xl-between { + align-content: space-between !important; + } + + .align-content-xl-around { + align-content: space-around !important; + } + + .align-content-xl-stretch { + align-content: stretch !important; + } + + .align-self-xl-auto { + align-self: auto !important; + } + + .align-self-xl-start { + align-self: flex-start !important; + } + + .align-self-xl-end { + align-self: flex-end !important; + } + + .align-self-xl-center { + align-self: center !important; + } + + .align-self-xl-baseline { + align-self: baseline !important; + } + + .align-self-xl-stretch { + align-self: stretch !important; + } + + .order-xl-first { + order: -1 !important; + } + + .order-xl-0 { + order: 0 !important; + } + + .order-xl-1 { + order: 1 !important; + } + + .order-xl-2 { + order: 2 !important; + } + + .order-xl-3 { + order: 3 !important; + } + + .order-xl-4 { + order: 4 !important; + } + + .order-xl-5 { + order: 5 !important; + } + + .order-xl-last { + order: 6 !important; + } + + .m-xl-0 { + margin: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mx-xl-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-xl-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-xl-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-xl-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-xl-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-xl-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-xl-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-xl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xl-0 { + margin-top: 0 !important; + } + + .mt-xl-1 { + margin-top: 0.25rem !important; + } + + .mt-xl-2 { + margin-top: 0.5rem !important; + } + + .mt-xl-3 { + margin-top: 1rem !important; + } + + .mt-xl-4 { + margin-top: 1.5rem !important; + } + + .mt-xl-5 { + margin-top: 3rem !important; + } + + .mt-xl-auto { + margin-top: auto !important; + } + + .me-xl-0 { + margin-left: 0 !important; + } + + .me-xl-1 { + margin-left: 0.25rem !important; + } + + .me-xl-2 { + margin-left: 0.5rem !important; + } + + .me-xl-3 { + margin-left: 1rem !important; + } + + .me-xl-4 { + margin-left: 1.5rem !important; + } + + .me-xl-5 { + margin-left: 3rem !important; + } + + .me-xl-auto { + margin-left: auto !important; + } + + .mb-xl-0 { + margin-bottom: 0 !important; + } + + .mb-xl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xl-3 { + margin-bottom: 1rem !important; + } + + .mb-xl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xl-5 { + margin-bottom: 3rem !important; + } + + .mb-xl-auto { + margin-bottom: auto !important; + } + + .ms-xl-0 { + margin-right: 0 !important; + } + + .ms-xl-1 { + margin-right: 0.25rem !important; + } + + .ms-xl-2 { + margin-right: 0.5rem !important; + } + + .ms-xl-3 { + margin-right: 1rem !important; + } + + .ms-xl-4 { + margin-right: 1.5rem !important; + } + + .ms-xl-5 { + margin-right: 3rem !important; + } + + .ms-xl-auto { + margin-right: auto !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .px-xl-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-xl-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-xl-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-xl-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-xl-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-xl-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-xl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xl-0 { + padding-top: 0 !important; + } + + .pt-xl-1 { + padding-top: 0.25rem !important; + } + + .pt-xl-2 { + padding-top: 0.5rem !important; + } + + .pt-xl-3 { + padding-top: 1rem !important; + } + + .pt-xl-4 { + padding-top: 1.5rem !important; + } + + .pt-xl-5 { + padding-top: 3rem !important; + } + + .pe-xl-0 { + padding-left: 0 !important; + } + + .pe-xl-1 { + padding-left: 0.25rem !important; + } + + .pe-xl-2 { + padding-left: 0.5rem !important; + } + + .pe-xl-3 { + padding-left: 1rem !important; + } + + .pe-xl-4 { + padding-left: 1.5rem !important; + } + + .pe-xl-5 { + padding-left: 3rem !important; + } + + .pb-xl-0 { + padding-bottom: 0 !important; + } + + .pb-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xl-3 { + padding-bottom: 1rem !important; + } + + .pb-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xl-5 { + padding-bottom: 3rem !important; + } + + .ps-xl-0 { + padding-right: 0 !important; + } + + .ps-xl-1 { + padding-right: 0.25rem !important; + } + + .ps-xl-2 { + padding-right: 0.5rem !important; + } + + .ps-xl-3 { + padding-right: 1rem !important; + } + + .ps-xl-4 { + padding-right: 1.5rem !important; + } + + .ps-xl-5 { + padding-right: 3rem !important; + } + + .gap-xl-0 { + gap: 0 !important; + } + + .gap-xl-1 { + gap: 0.25rem !important; + } + + .gap-xl-2 { + gap: 0.5rem !important; + } + + .gap-xl-3 { + gap: 1rem !important; + } + + .gap-xl-4 { + gap: 1.5rem !important; + } + + .gap-xl-5 { + gap: 3rem !important; + } + + .row-gap-xl-0 { + row-gap: 0 !important; + } + + .row-gap-xl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xl-3 { + row-gap: 1rem !important; + } + + .row-gap-xl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xl-5 { + row-gap: 3rem !important; + } + + .column-gap-xl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xl-start { + text-align: right !important; + } + + .text-xl-end { + text-align: left !important; + } + + .text-xl-center { + text-align: center !important; + } +} + +@media (min-width: 1400px) { + .float-xxl-start { + float: right !important; + } + + .float-xxl-end { + float: left !important; + } + + .float-xxl-none { + float: none !important; + } + + .object-fit-xxl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + + .object-fit-xxl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + + .object-fit-xxl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + + .object-fit-xxl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + + .object-fit-xxl-none { + -o-object-fit: none !important; + object-fit: none !important; + } + + .d-xxl-inline { + display: inline !important; + } + + .d-xxl-inline-block { + display: inline-block !important; + } + + .d-xxl-block { + display: block !important; + } + + .d-xxl-grid { + display: grid !important; + } + + .d-xxl-inline-grid { + display: inline-grid !important; + } + + .d-xxl-table { + display: table !important; + } + + .d-xxl-table-row { + display: table-row !important; + } + + .d-xxl-table-cell { + display: table-cell !important; + } + + .d-xxl-flex { + display: flex !important; + } + + .d-xxl-inline-flex { + display: inline-flex !important; + } + + .d-xxl-none { + display: none !important; + } + + .flex-xxl-fill { + flex: 1 1 auto !important; + } + + .flex-xxl-row { + flex-direction: row !important; + } + + .flex-xxl-column { + flex-direction: column !important; + } + + .flex-xxl-row-reverse { + flex-direction: row-reverse !important; + } + + .flex-xxl-column-reverse { + flex-direction: column-reverse !important; + } + + .flex-xxl-grow-0 { + flex-grow: 0 !important; + } + + .flex-xxl-grow-1 { + flex-grow: 1 !important; + } + + .flex-xxl-shrink-0 { + flex-shrink: 0 !important; + } + + .flex-xxl-shrink-1 { + flex-shrink: 1 !important; + } + + .flex-xxl-wrap { + flex-wrap: wrap !important; + } + + .flex-xxl-nowrap { + flex-wrap: nowrap !important; + } + + .flex-xxl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + + .justify-content-xxl-start { + justify-content: flex-start !important; + } + + .justify-content-xxl-end { + justify-content: flex-end !important; + } + + .justify-content-xxl-center { + justify-content: center !important; + } + + .justify-content-xxl-between { + justify-content: space-between !important; + } + + .justify-content-xxl-around { + justify-content: space-around !important; + } + + .justify-content-xxl-evenly { + justify-content: space-evenly !important; + } + + .align-items-xxl-start { + align-items: flex-start !important; + } + + .align-items-xxl-end { + align-items: flex-end !important; + } + + .align-items-xxl-center { + align-items: center !important; + } + + .align-items-xxl-baseline { + align-items: baseline !important; + } + + .align-items-xxl-stretch { + align-items: stretch !important; + } + + .align-content-xxl-start { + align-content: flex-start !important; + } + + .align-content-xxl-end { + align-content: flex-end !important; + } + + .align-content-xxl-center { + align-content: center !important; + } + + .align-content-xxl-between { + align-content: space-between !important; + } + + .align-content-xxl-around { + align-content: space-around !important; + } + + .align-content-xxl-stretch { + align-content: stretch !important; + } + + .align-self-xxl-auto { + align-self: auto !important; + } + + .align-self-xxl-start { + align-self: flex-start !important; + } + + .align-self-xxl-end { + align-self: flex-end !important; + } + + .align-self-xxl-center { + align-self: center !important; + } + + .align-self-xxl-baseline { + align-self: baseline !important; + } + + .align-self-xxl-stretch { + align-self: stretch !important; + } + + .order-xxl-first { + order: -1 !important; + } + + .order-xxl-0 { + order: 0 !important; + } + + .order-xxl-1 { + order: 1 !important; + } + + .order-xxl-2 { + order: 2 !important; + } + + .order-xxl-3 { + order: 3 !important; + } + + .order-xxl-4 { + order: 4 !important; + } + + .order-xxl-5 { + order: 5 !important; + } + + .order-xxl-last { + order: 6 !important; + } + + .m-xxl-0 { + margin: 0 !important; + } + + .m-xxl-1 { + margin: 0.25rem !important; + } + + .m-xxl-2 { + margin: 0.5rem !important; + } + + .m-xxl-3 { + margin: 1rem !important; + } + + .m-xxl-4 { + margin: 1.5rem !important; + } + + .m-xxl-5 { + margin: 3rem !important; + } + + .m-xxl-auto { + margin: auto !important; + } + + .mx-xxl-0 { + margin-left: 0 !important; + margin-right: 0 !important; + } + + .mx-xxl-1 { + margin-left: 0.25rem !important; + margin-right: 0.25rem !important; + } + + .mx-xxl-2 { + margin-left: 0.5rem !important; + margin-right: 0.5rem !important; + } + + .mx-xxl-3 { + margin-left: 1rem !important; + margin-right: 1rem !important; + } + + .mx-xxl-4 { + margin-left: 1.5rem !important; + margin-right: 1.5rem !important; + } + + .mx-xxl-5 { + margin-left: 3rem !important; + margin-right: 3rem !important; + } + + .mx-xxl-auto { + margin-left: auto !important; + margin-right: auto !important; + } + + .my-xxl-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; + } + + .my-xxl-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; + } + + .my-xxl-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; + } + + .my-xxl-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; + } + + .my-xxl-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; + } + + .my-xxl-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; + } + + .my-xxl-auto { + margin-top: auto !important; + margin-bottom: auto !important; + } + + .mt-xxl-0 { + margin-top: 0 !important; + } + + .mt-xxl-1 { + margin-top: 0.25rem !important; + } + + .mt-xxl-2 { + margin-top: 0.5rem !important; + } + + .mt-xxl-3 { + margin-top: 1rem !important; + } + + .mt-xxl-4 { + margin-top: 1.5rem !important; + } + + .mt-xxl-5 { + margin-top: 3rem !important; + } + + .mt-xxl-auto { + margin-top: auto !important; + } + + .me-xxl-0 { + margin-left: 0 !important; + } + + .me-xxl-1 { + margin-left: 0.25rem !important; + } + + .me-xxl-2 { + margin-left: 0.5rem !important; + } + + .me-xxl-3 { + margin-left: 1rem !important; + } + + .me-xxl-4 { + margin-left: 1.5rem !important; + } + + .me-xxl-5 { + margin-left: 3rem !important; + } + + .me-xxl-auto { + margin-left: auto !important; + } + + .mb-xxl-0 { + margin-bottom: 0 !important; + } + + .mb-xxl-1 { + margin-bottom: 0.25rem !important; + } + + .mb-xxl-2 { + margin-bottom: 0.5rem !important; + } + + .mb-xxl-3 { + margin-bottom: 1rem !important; + } + + .mb-xxl-4 { + margin-bottom: 1.5rem !important; + } + + .mb-xxl-5 { + margin-bottom: 3rem !important; + } + + .mb-xxl-auto { + margin-bottom: auto !important; + } + + .ms-xxl-0 { + margin-right: 0 !important; + } + + .ms-xxl-1 { + margin-right: 0.25rem !important; + } + + .ms-xxl-2 { + margin-right: 0.5rem !important; + } + + .ms-xxl-3 { + margin-right: 1rem !important; + } + + .ms-xxl-4 { + margin-right: 1.5rem !important; + } + + .ms-xxl-5 { + margin-right: 3rem !important; + } + + .ms-xxl-auto { + margin-right: auto !important; + } + + .p-xxl-0 { + padding: 0 !important; + } + + .p-xxl-1 { + padding: 0.25rem !important; + } + + .p-xxl-2 { + padding: 0.5rem !important; + } + + .p-xxl-3 { + padding: 1rem !important; + } + + .p-xxl-4 { + padding: 1.5rem !important; + } + + .p-xxl-5 { + padding: 3rem !important; + } + + .px-xxl-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .px-xxl-1 { + padding-left: 0.25rem !important; + padding-right: 0.25rem !important; + } + + .px-xxl-2 { + padding-left: 0.5rem !important; + padding-right: 0.5rem !important; + } + + .px-xxl-3 { + padding-left: 1rem !important; + padding-right: 1rem !important; + } + + .px-xxl-4 { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } + + .px-xxl-5 { + padding-left: 3rem !important; + padding-right: 3rem !important; + } + + .py-xxl-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; + } + + .py-xxl-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; + } + + .py-xxl-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + } + + .py-xxl-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; + } + + .py-xxl-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; + } + + .py-xxl-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; + } + + .pt-xxl-0 { + padding-top: 0 !important; + } + + .pt-xxl-1 { + padding-top: 0.25rem !important; + } + + .pt-xxl-2 { + padding-top: 0.5rem !important; + } + + .pt-xxl-3 { + padding-top: 1rem !important; + } + + .pt-xxl-4 { + padding-top: 1.5rem !important; + } + + .pt-xxl-5 { + padding-top: 3rem !important; + } + + .pe-xxl-0 { + padding-left: 0 !important; + } + + .pe-xxl-1 { + padding-left: 0.25rem !important; + } + + .pe-xxl-2 { + padding-left: 0.5rem !important; + } + + .pe-xxl-3 { + padding-left: 1rem !important; + } + + .pe-xxl-4 { + padding-left: 1.5rem !important; + } + + .pe-xxl-5 { + padding-left: 3rem !important; + } + + .pb-xxl-0 { + padding-bottom: 0 !important; + } + + .pb-xxl-1 { + padding-bottom: 0.25rem !important; + } + + .pb-xxl-2 { + padding-bottom: 0.5rem !important; + } + + .pb-xxl-3 { + padding-bottom: 1rem !important; + } + + .pb-xxl-4 { + padding-bottom: 1.5rem !important; + } + + .pb-xxl-5 { + padding-bottom: 3rem !important; + } + + .ps-xxl-0 { + padding-right: 0 !important; + } + + .ps-xxl-1 { + padding-right: 0.25rem !important; + } + + .ps-xxl-2 { + padding-right: 0.5rem !important; + } + + .ps-xxl-3 { + padding-right: 1rem !important; + } + + .ps-xxl-4 { + padding-right: 1.5rem !important; + } + + .ps-xxl-5 { + padding-right: 3rem !important; + } + + .gap-xxl-0 { + gap: 0 !important; + } + + .gap-xxl-1 { + gap: 0.25rem !important; + } + + .gap-xxl-2 { + gap: 0.5rem !important; + } + + .gap-xxl-3 { + gap: 1rem !important; + } + + .gap-xxl-4 { + gap: 1.5rem !important; + } + + .gap-xxl-5 { + gap: 3rem !important; + } + + .row-gap-xxl-0 { + row-gap: 0 !important; + } + + .row-gap-xxl-1 { + row-gap: 0.25rem !important; + } + + .row-gap-xxl-2 { + row-gap: 0.5rem !important; + } + + .row-gap-xxl-3 { + row-gap: 1rem !important; + } + + .row-gap-xxl-4 { + row-gap: 1.5rem !important; + } + + .row-gap-xxl-5 { + row-gap: 3rem !important; + } + + .column-gap-xxl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + + .column-gap-xxl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + + .column-gap-xxl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + + .column-gap-xxl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + + .column-gap-xxl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + + .column-gap-xxl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } + + .text-xxl-start { + text-align: right !important; + } + + .text-xxl-end { + text-align: left !important; + } + + .text-xxl-center { + text-align: center !important; + } } + @media (min-width: 1200px) { - .fs-1 { - font-size: 2.5rem !important; - } - .fs-2 { - font-size: 2rem !important; - } - .fs-3 { - font-size: 1.75rem !important; - } - .fs-4 { - font-size: 1.5rem !important; - } + .fs-1 { + font-size: 2.5rem !important; + } + + .fs-2 { + font-size: 2rem !important; + } + + .fs-3 { + font-size: 1.75rem !important; + } + + .fs-4 { + font-size: 1.5rem !important; + } } + @media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-grid { + display: grid !important; + } + + .d-print-inline-grid { + display: inline-grid !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: flex !important; + } + + .d-print-inline-flex { + display: inline-flex !important; + } + + .d-print-none { + display: none !important; + } } -/*# sourceMappingURL=bootstrap.rtl.css.map */ \ No newline at end of file + +/*# sourceMappingURL=bootstrap.rtl.css.map */ diff --git a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js index 6294dff3df..e317fdf9ef 100644 --- a/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js +++ b/src/examples/ReactiveUI.Builder.BlazorServer/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js @@ -4,6311 +4,6507 @@ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory()); -})(this, (function () { 'use strict'; - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/data.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - /** - * Constants - */ - - const elementMap = new Map(); - const Data = { - set(element, key, instance) { - if (!elementMap.has(element)) { - elementMap.set(element, new Map()); - } - const instanceMap = elementMap.get(element); - - // make it clear we only want one instance per element - // can be removed later when multiple key/instances are fine to be used - if (!instanceMap.has(key) && instanceMap.size !== 0) { - // eslint-disable-next-line no-console - console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); - return; - } - instanceMap.set(key, instance); - }, - get(element, key) { - if (elementMap.has(element)) { - return elementMap.get(element).get(key) || null; - } - return null; - }, - remove(element, key) { - if (!elementMap.has(element)) { - return; - } - const instanceMap = elementMap.get(element); - instanceMap.delete(key); - - // free up element references if there are no instances left for an element - if (instanceMap.size === 0) { - elementMap.delete(element); - } + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory()); +})(this, (function () { + 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/data.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + /** + * Constants + */ + + const elementMap = new Map(); + const Data = { + set(element, key, instance) { + if (!elementMap.has(element)) { + elementMap.set(element, new Map()); + } + const instanceMap = elementMap.get(element); + + // make it clear we only want one instance per element + // can be removed later when multiple key/instances are fine to be used + if (!instanceMap.has(key) && instanceMap.size !== 0) { + // eslint-disable-next-line no-console + console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); + return; + } + instanceMap.set(key, instance); + }, + get(element, key) { + if (elementMap.has(element)) { + return elementMap.get(element).get(key) || null; + } + return null; + }, + remove(element, key) { + if (!elementMap.has(element)) { + return; + } + const instanceMap = elementMap.get(element); + instanceMap.delete(key); + + // free up element references if there are no instances left for an element + if (instanceMap.size === 0) { + elementMap.delete(element); + } + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/index.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const MAX_UID = 1000000; + const MILLISECONDS_MULTIPLIER = 1000; + const TRANSITION_END = 'transitionend'; + + /** + * Properly escape IDs selectors to handle weird IDs + * @param {string} selector + * @returns {string} + */ + const parseSelector = selector => { + if (selector && window.CSS && window.CSS.escape) { + // document.querySelector needs escaping to handle IDs (html5+) containing for instance / + selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); + } + return selector; + }; + + // Shout-out Angus Croll (https://goo.gl/pxwQGp) + const toType = object => { + if (object === null || object === undefined) { + return `${object}`; + } + return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); + }; + + /** + * Public Util API + */ + + const getUID = prefix => { + do { + prefix += Math.floor(Math.random() * MAX_UID); + } while (document.getElementById(prefix)); + return prefix; + }; + const getTransitionDurationFromElement = element => { + if (!element) { + return 0; + } + + // Get transition-duration of the element + let { + transitionDuration, + transitionDelay + } = window.getComputedStyle(element); + const floatTransitionDuration = Number.parseFloat(transitionDuration); + const floatTransitionDelay = Number.parseFloat(transitionDelay); + + // Return 0 if element or transition duration is not found + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0; + } + + // If multiple durations are defined, take the first + transitionDuration = transitionDuration.split(',')[0]; + transitionDelay = transitionDelay.split(',')[0]; + return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; + }; + const triggerTransitionEnd = element => { + element.dispatchEvent(new Event(TRANSITION_END)); + }; + const isElement$1 = object => { + if (!object || typeof object !== 'object') { + return false; + } + if (typeof object.jquery !== 'undefined') { + object = object[0]; + } + return typeof object.nodeType !== 'undefined'; + }; + const getElement = object => { + // it's a jQuery object or a node element + if (isElement$1(object)) { + return object.jquery ? object[0] : object; + } + if (typeof object === 'string' && object.length > 0) { + return document.querySelector(parseSelector(object)); + } + return null; + }; + const isVisible = element => { + if (!isElement$1(element) || element.getClientRects().length === 0) { + return false; + } + const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; + // Handle `details` element as its content may falsie appear visible when it is closed + const closedDetails = element.closest('details:not([open])'); + if (!closedDetails) { + return elementIsVisible; + } + if (closedDetails !== element) { + const summary = element.closest('summary'); + if (summary && summary.parentNode !== closedDetails) { + return false; + } + if (summary === null) { + return false; + } + } + return elementIsVisible; + }; + const isDisabled = element => { + if (!element || element.nodeType !== Node.ELEMENT_NODE) { + return true; + } + if (element.classList.contains('disabled')) { + return true; + } + if (typeof element.disabled !== 'undefined') { + return element.disabled; + } + return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; + }; + const findShadowRoot = element => { + if (!document.documentElement.attachShadow) { + return null; + } + + // Can find the shadow root otherwise it'll return the document + if (typeof element.getRootNode === 'function') { + const root = element.getRootNode(); + return root instanceof ShadowRoot ? root : null; + } + if (element instanceof ShadowRoot) { + return element; + } + + // when we don't find a shadow root + if (!element.parentNode) { + return null; + } + return findShadowRoot(element.parentNode); + }; + const noop = () => { + }; + + /** + * Trick to restart an element's animation + * + * @param {HTMLElement} element + * @return void + * + * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation + */ + const reflow = element => { + element.offsetHeight; // eslint-disable-line no-unused-expressions + }; + const getjQuery = () => { + if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { + return window.jQuery; + } + return null; + }; + const DOMContentLoadedCallbacks = []; + const onDOMContentLoaded = callback => { + if (document.readyState === 'loading') { + // add listener on the first call when the document is in loading state + if (!DOMContentLoadedCallbacks.length) { + document.addEventListener('DOMContentLoaded', () => { + for (const callback of DOMContentLoadedCallbacks) { + callback(); + } + }); + } + DOMContentLoadedCallbacks.push(callback); + } else { + callback(); + } + }; + const isRTL = () => document.documentElement.dir === 'rtl'; + const defineJQueryPlugin = plugin => { + onDOMContentLoaded(() => { + const $ = getjQuery(); + /* istanbul ignore if */ + if ($) { + const name = plugin.NAME; + const JQUERY_NO_CONFLICT = $.fn[name]; + $.fn[name] = plugin.jQueryInterface; + $.fn[name].Constructor = plugin; + $.fn[name].noConflict = () => { + $.fn[name] = JQUERY_NO_CONFLICT; + return plugin.jQueryInterface; + }; + } + }); + }; + const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { + return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue; + }; + const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { + if (!waitForTransition) { + execute(callback); + return; + } + const durationPadding = 5; + const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; + let called = false; + const handler = ({ + target + }) => { + if (target !== transitionElement) { + return; + } + called = true; + transitionElement.removeEventListener(TRANSITION_END, handler); + execute(callback); + }; + transitionElement.addEventListener(TRANSITION_END, handler); + setTimeout(() => { + if (!called) { + triggerTransitionEnd(transitionElement); + } + }, emulatedDuration); + }; + + /** + * Return the previous/next element of a list. + * + * @param {array} list The list of elements + * @param activeElement The active element + * @param shouldGetNext Choose to get next or previous element + * @param isCycleAllowed + * @return {Element|elem} The proper element + */ + const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { + const listLength = list.length; + let index = list.indexOf(activeElement); + + // if the element does not exist in the list return an element + // depending on the direction and if cycle is allowed + if (index === -1) { + return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; + } + index += shouldGetNext ? 1 : -1; + if (isCycleAllowed) { + index = (index + listLength) % listLength; + } + return list[Math.max(0, Math.min(index, listLength - 1))]; + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/event-handler.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const namespaceRegex = /[^.]*(?=\..*)\.|.*/; + const stripNameRegex = /\..*/; + const stripUidRegex = /::\d+$/; + const eventRegistry = {}; // Events storage + let uidEvent = 1; + const customEvents = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); + + /** + * Private methods + */ + + function makeEventUid(element, uid) { + return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; + } + + function getElementEvents(element) { + const uid = makeEventUid(element); + element.uidEvent = uid; + eventRegistry[uid] = eventRegistry[uid] || {}; + return eventRegistry[uid]; + } + + function bootstrapHandler(element, fn) { + return function handler(event) { + hydrateObj(event, { + delegateTarget: element + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, fn); + } + return fn.apply(element, [event]); + }; + } + + function bootstrapDelegationHandler(element, selector, fn) { + return function handler(event) { + const domElements = element.querySelectorAll(selector); + for (let { + target + } = event; target && target !== this; target = target.parentNode) { + for (const domElement of domElements) { + if (domElement !== target) { + continue; + } + hydrateObj(event, { + delegateTarget: target + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, selector, fn); + } + return fn.apply(target, [event]); + } + } + }; + } + + function findHandler(events, callable, delegationSelector = null) { + return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); + } + + function normalizeParameters(originalTypeEvent, handler, delegationFunction) { + const isDelegated = typeof handler === 'string'; + // TODO: tooltip passes `false` instead of selector, so we need to check + const callable = isDelegated ? delegationFunction : handler || delegationFunction; + let typeEvent = getTypeEvent(originalTypeEvent); + if (!nativeEvents.has(typeEvent)) { + typeEvent = originalTypeEvent; + } + return [isDelegated, callable, typeEvent]; + } + + function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + + // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position + // this prevents the handler from being dispatched the same way as mouseover or mouseout does + if (originalTypeEvent in customEvents) { + const wrapFunction = fn => { + return function (event) { + if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { + return fn.call(this, event); + } + }; + }; + callable = wrapFunction(callable); + } + const events = getElementEvents(element); + const handlers = events[typeEvent] || (events[typeEvent] = {}); + const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); + if (previousFunction) { + previousFunction.oneOff = previousFunction.oneOff && oneOff; + return; + } + const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); + const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); + fn.delegationSelector = isDelegated ? handler : null; + fn.callable = callable; + fn.oneOff = oneOff; + fn.uidEvent = uid; + handlers[uid] = fn; + element.addEventListener(typeEvent, fn, isDelegated); + } + + function removeHandler(element, events, typeEvent, handler, delegationSelector) { + const fn = findHandler(events[typeEvent], handler, delegationSelector); + if (!fn) { + return; + } + element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); + delete events[typeEvent][fn.uidEvent]; + } + + function removeNamespacedHandlers(element, events, typeEvent, namespace) { + const storeElementEvent = events[typeEvent] || {}; + for (const [handlerKey, event] of Object.entries(storeElementEvent)) { + if (handlerKey.includes(namespace)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + } + + function getTypeEvent(event) { + // allow to get the native events from namespaced events ('click.bs.button' --> 'click') + event = event.replace(stripNameRegex, ''); + return customEvents[event] || event; + } + + const EventHandler = { + on(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, false); + }, + one(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, true); + }, + off(element, originalTypeEvent, handler, delegationFunction) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + const inNamespace = typeEvent !== originalTypeEvent; + const events = getElementEvents(element); + const storeElementEvent = events[typeEvent] || {}; + const isNamespace = originalTypeEvent.startsWith('.'); + if (typeof callable !== 'undefined') { + // Simplest case: handler is passed, remove that listener ONLY. + if (!Object.keys(storeElementEvent).length) { + return; + } + removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); + return; + } + if (isNamespace) { + for (const elementEvent of Object.keys(events)) { + removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); + } + } + for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { + const handlerKey = keyHandlers.replace(stripUidRegex, ''); + if (!inNamespace || originalTypeEvent.includes(handlerKey)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + }, + trigger(element, event, args) { + if (typeof event !== 'string' || !element) { + return null; + } + const $ = getjQuery(); + const typeEvent = getTypeEvent(event); + const inNamespace = event !== typeEvent; + let jQueryEvent = null; + let bubbles = true; + let nativeDispatch = true; + let defaultPrevented = false; + if (inNamespace && $) { + jQueryEvent = $.Event(event, args); + $(element).trigger(jQueryEvent); + bubbles = !jQueryEvent.isPropagationStopped(); + nativeDispatch = !jQueryEvent.isImmediatePropagationStopped(); + defaultPrevented = jQueryEvent.isDefaultPrevented(); + } + const evt = hydrateObj(new Event(event, { + bubbles, + cancelable: true + }), args); + if (defaultPrevented) { + evt.preventDefault(); + } + if (nativeDispatch) { + element.dispatchEvent(evt); + } + if (evt.defaultPrevented && jQueryEvent) { + jQueryEvent.preventDefault(); + } + return evt; + } + }; + + function hydrateObj(obj, meta = {}) { + for (const [key, value] of Object.entries(meta)) { + try { + obj[key] = value; + } catch (_unused) { + Object.defineProperty(obj, key, { + configurable: true, + get() { + return value; + } + }); + } + } + return obj; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/manipulator.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + function normalizeData(value) { + if (value === 'true') { + return true; + } + if (value === 'false') { + return false; + } + if (value === Number(value).toString()) { + return Number(value); + } + if (value === '' || value === 'null') { + return null; + } + if (typeof value !== 'string') { + return value; + } + try { + return JSON.parse(decodeURIComponent(value)); + } catch (_unused) { + return value; + } + } + + function normalizeDataKey(key) { + return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); + } + + const Manipulator = { + setDataAttribute(element, key, value) { + element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); + }, + removeDataAttribute(element, key) { + element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); + }, + getDataAttributes(element) { + if (!element) { + return {}; + } + const attributes = {}; + const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig')); + for (const key of bsKeys) { + let pureKey = key.replace(/^bs/, ''); + pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length); + attributes[pureKey] = normalizeData(element.dataset[key]); + } + return attributes; + }, + getDataAttribute(element, key) { + return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/config.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Class definition + */ + + class Config { + // Getters + static get Default() { + return {}; + } + + static get DefaultType() { + return {}; + } + + static get NAME() { + throw new Error('You have to implement the static method "NAME", for each component!'); + } + + _getConfig(config) { + config = this._mergeConfigObj(config); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + + _configAfterMerge(config) { + return config; + } + + _mergeConfigObj(config, element) { + const jsonConfig = isElement$1(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse + + return { + ...this.constructor.Default, + ...(typeof jsonConfig === 'object' ? jsonConfig : {}), + ...(isElement$1(element) ? Manipulator.getDataAttributes(element) : {}), + ...(typeof config === 'object' ? config : {}) + }; + } + + _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { + for (const [property, expectedTypes] of Object.entries(configTypes)) { + const value = config[property]; + const valueType = isElement$1(value) ? 'element' : toType(value); + if (!new RegExp(expectedTypes).test(valueType)) { + throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); + } + } + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap base-component.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const VERSION = '5.3.3'; + + /** + * Class definition + */ + + class BaseComponent extends Config { + constructor(element, config) { + super(); + element = getElement(element); + if (!element) { + return; + } + this._element = element; + this._config = this._getConfig(config); + Data.set(this._element, this.constructor.DATA_KEY, this); + } + + // Public + dispose() { + Data.remove(this._element, this.constructor.DATA_KEY); + EventHandler.off(this._element, this.constructor.EVENT_KEY); + for (const propertyName of Object.getOwnPropertyNames(this)) { + this[propertyName] = null; + } + } + + _queueCallback(callback, element, isAnimated = true) { + executeAfterTransition(callback, element, isAnimated); + } + + _getConfig(config) { + config = this._mergeConfigObj(config, this._element); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + + // Static + static getInstance(element) { + return Data.get(getElement(element), this.DATA_KEY); + } + + static getOrCreateInstance(element, config = {}) { + return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); + } + + static get VERSION() { + return VERSION; + } + + static get DATA_KEY() { + return `bs.${this.NAME}`; + } + + static get EVENT_KEY() { + return `.${this.DATA_KEY}`; + } + + static eventName(name) { + return `${name}${this.EVENT_KEY}`; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/selector-engine.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const getSelector = element => { + let selector = element.getAttribute('data-bs-target'); + if (!selector || selector === '#') { + let hrefAttribute = element.getAttribute('href'); + + // The only valid content that could double as a selector are IDs or classes, + // so everything starting with `#` or `.`. If a "real" URL is used as the selector, + // `document.querySelector` will rightfully complain it is invalid. + // See https://github.com/twbs/bootstrap/issues/32273 + if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { + return null; + } + + // Just in case some CMS puts out a full URL with the anchor appended + if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { + hrefAttribute = `#${hrefAttribute.split('#')[1]}`; + } + selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; + } + return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null; + }; + const SelectorEngine = { + find(selector, element = document.documentElement) { + return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); + }, + findOne(selector, element = document.documentElement) { + return Element.prototype.querySelector.call(element, selector); + }, + children(element, selector) { + return [].concat(...element.children).filter(child => child.matches(selector)); + }, + parents(element, selector) { + const parents = []; + let ancestor = element.parentNode.closest(selector); + while (ancestor) { + parents.push(ancestor); + ancestor = ancestor.parentNode.closest(selector); + } + return parents; + }, + prev(element, selector) { + let previous = element.previousElementSibling; + while (previous) { + if (previous.matches(selector)) { + return [previous]; + } + previous = previous.previousElementSibling; + } + return []; + }, + // TODO: this is now unused; remove later along with prev() + next(element, selector) { + let next = element.nextElementSibling; + while (next) { + if (next.matches(selector)) { + return [next]; + } + next = next.nextElementSibling; + } + return []; + }, + focusableChildren(element) { + const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); + return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el)); + }, + getSelectorFromElement(element) { + const selector = getSelector(element); + if (selector) { + return SelectorEngine.findOne(selector) ? selector : null; + } + return null; + }, + getElementFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.findOne(selector) : null; + }, + getMultipleElementsFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.find(selector) : []; + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/component-functions.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const enableDismissTrigger = (component, method = 'hide') => { + const clickEvent = `click.dismiss${component.EVENT_KEY}`; + const name = component.NAME; + EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + if (isDisabled(this)) { + return; + } + const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`); + const instance = component.getOrCreateInstance(target); + + // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method + instance[method](); + }); + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap alert.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$f = 'alert'; + const DATA_KEY$a = 'bs.alert'; + const EVENT_KEY$b = `.${DATA_KEY$a}`; + const EVENT_CLOSE = `close${EVENT_KEY$b}`; + const EVENT_CLOSED = `closed${EVENT_KEY$b}`; + const CLASS_NAME_FADE$5 = 'fade'; + const CLASS_NAME_SHOW$8 = 'show'; + + /** + * Class definition + */ + + class Alert extends BaseComponent { + // Getters + static get NAME() { + return NAME$f; + } + + // Public + close() { + const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE); + if (closeEvent.defaultPrevented) { + return; + } + this._element.classList.remove(CLASS_NAME_SHOW$8); + const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5); + this._queueCallback(() => this._destroyElement(), this._element, isAnimated); + } + + // Private + _destroyElement() { + this._element.remove(); + EventHandler.trigger(this._element, EVENT_CLOSED); + this.dispose(); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Alert.getOrCreateInstance(this); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](this); + }); + } + } + + /** + * Data API implementation + */ + + enableDismissTrigger(Alert, 'close'); + + /** + * jQuery + */ + + defineJQueryPlugin(Alert); + + /** + * -------------------------------------------------------------------------- + * Bootstrap button.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$e = 'button'; + const DATA_KEY$9 = 'bs.button'; + const EVENT_KEY$a = `.${DATA_KEY$9}`; + const DATA_API_KEY$6 = '.data-api'; + const CLASS_NAME_ACTIVE$3 = 'active'; + const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle="button"]'; + const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`; + + /** + * Class definition + */ + + class Button extends BaseComponent { + // Getters + static get NAME() { + return NAME$e; + } + + // Public + toggle() { + // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method + this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3)); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Button.getOrCreateInstance(this); + if (config === 'toggle') { + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => { + event.preventDefault(); + const button = event.target.closest(SELECTOR_DATA_TOGGLE$5); + const data = Button.getOrCreateInstance(button); + data.toggle(); + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Button); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/swipe.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$d = 'swipe'; + const EVENT_KEY$9 = '.bs.swipe'; + const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`; + const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`; + const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`; + const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`; + const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`; + const POINTER_TYPE_TOUCH = 'touch'; + const POINTER_TYPE_PEN = 'pen'; + const CLASS_NAME_POINTER_EVENT = 'pointer-event'; + const SWIPE_THRESHOLD = 40; + const Default$c = { + endCallback: null, + leftCallback: null, + rightCallback: null + }; + const DefaultType$c = { + endCallback: '(function|null)', + leftCallback: '(function|null)', + rightCallback: '(function|null)' + }; + + /** + * Class definition + */ + + class Swipe extends Config { + constructor(element, config) { + super(); + this._element = element; + if (!element || !Swipe.isSupported()) { + return; + } + this._config = this._getConfig(config); + this._deltaX = 0; + this._supportPointerEvents = Boolean(window.PointerEvent); + this._initEvents(); + } + + // Getters + static get Default() { + return Default$c; + } + + static get DefaultType() { + return DefaultType$c; + } + + static get NAME() { + return NAME$d; + } + + // Public + dispose() { + EventHandler.off(this._element, EVENT_KEY$9); + } + + // Private + _start(event) { + if (!this._supportPointerEvents) { + this._deltaX = event.touches[0].clientX; + return; + } + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX; + } + } + + _end(event) { + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX - this._deltaX; + } + this._handleSwipe(); + execute(this._config.endCallback); + } + + _move(event) { + this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX; + } + + _handleSwipe() { + const absDeltaX = Math.abs(this._deltaX); + if (absDeltaX <= SWIPE_THRESHOLD) { + return; + } + const direction = absDeltaX / this._deltaX; + this._deltaX = 0; + if (!direction) { + return; + } + execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback); + } + + _initEvents() { + if (this._supportPointerEvents) { + EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event)); + EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event)); + this._element.classList.add(CLASS_NAME_POINTER_EVENT); + } else { + EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event)); + EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event)); + EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event)); + } + } + + _eventIsPointerPenTouch(event) { + return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH); + } + + // Static + static isSupported() { + return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap carousel.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$c = 'carousel'; + const DATA_KEY$8 = 'bs.carousel'; + const EVENT_KEY$8 = `.${DATA_KEY$8}`; + const DATA_API_KEY$5 = '.data-api'; + const ARROW_LEFT_KEY$1 = 'ArrowLeft'; + const ARROW_RIGHT_KEY$1 = 'ArrowRight'; + const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch + + const ORDER_NEXT = 'next'; + const ORDER_PREV = 'prev'; + const DIRECTION_LEFT = 'left'; + const DIRECTION_RIGHT = 'right'; + const EVENT_SLIDE = `slide${EVENT_KEY$8}`; + const EVENT_SLID = `slid${EVENT_KEY$8}`; + const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`; + const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`; + const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`; + const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`; + const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`; + const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`; + const CLASS_NAME_CAROUSEL = 'carousel'; + const CLASS_NAME_ACTIVE$2 = 'active'; + const CLASS_NAME_SLIDE = 'slide'; + const CLASS_NAME_END = 'carousel-item-end'; + const CLASS_NAME_START = 'carousel-item-start'; + const CLASS_NAME_NEXT = 'carousel-item-next'; + const CLASS_NAME_PREV = 'carousel-item-prev'; + const SELECTOR_ACTIVE = '.active'; + const SELECTOR_ITEM = '.carousel-item'; + const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM; + const SELECTOR_ITEM_IMG = '.carousel-item img'; + const SELECTOR_INDICATORS = '.carousel-indicators'; + const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'; + const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'; + const KEY_TO_DIRECTION = { + [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT, + [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT + }; + const Default$b = { + interval: 5000, + keyboard: true, + pause: 'hover', + ride: false, + touch: true, + wrap: true + }; + const DefaultType$b = { + interval: '(number|boolean)', + // TODO:v6 remove boolean support + keyboard: 'boolean', + pause: '(string|boolean)', + ride: '(boolean|string)', + touch: 'boolean', + wrap: 'boolean' + }; + + /** + * Class definition + */ + + class Carousel extends BaseComponent { + constructor(element, config) { + super(element, config); + this._interval = null; + this._activeElement = null; + this._isSliding = false; + this.touchTimeout = null; + this._swipeHelper = null; + this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element); + this._addEventListeners(); + if (this._config.ride === CLASS_NAME_CAROUSEL) { + this.cycle(); + } + } + + // Getters + static get Default() { + return Default$b; + } + + static get DefaultType() { + return DefaultType$b; + } + + static get NAME() { + return NAME$c; + } + + // Public + next() { + this._slide(ORDER_NEXT); + } + + nextWhenVisible() { + // FIXME TODO use `document.visibilityState` + // Don't call next when the page isn't visible + // or the carousel or its parent isn't visible + if (!document.hidden && isVisible(this._element)) { + this.next(); + } + } + + prev() { + this._slide(ORDER_PREV); + } + + pause() { + if (this._isSliding) { + triggerTransitionEnd(this._element); + } + this._clearInterval(); + } + + cycle() { + this._clearInterval(); + this._updateInterval(); + this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval); + } + + _maybeEnableCycle() { + if (!this._config.ride) { + return; + } + if (this._isSliding) { + EventHandler.one(this._element, EVENT_SLID, () => this.cycle()); + return; + } + this.cycle(); + } + + to(index) { + const items = this._getItems(); + if (index > items.length - 1 || index < 0) { + return; + } + if (this._isSliding) { + EventHandler.one(this._element, EVENT_SLID, () => this.to(index)); + return; + } + const activeIndex = this._getItemIndex(this._getActive()); + if (activeIndex === index) { + return; + } + const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV; + this._slide(order, items[index]); + } + + dispose() { + if (this._swipeHelper) { + this._swipeHelper.dispose(); + } + super.dispose(); + } + + // Private + _configAfterMerge(config) { + config.defaultInterval = config.interval; + return config; + } + + _addEventListeners() { + if (this._config.keyboard) { + EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event)); + } + if (this._config.pause === 'hover') { + EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause()); + EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle()); + } + if (this._config.touch && Swipe.isSupported()) { + this._addTouchEventListeners(); + } + } + + _addTouchEventListeners() { + for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { + EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault()); + } + const endCallBack = () => { + if (this._config.pause !== 'hover') { + return; + } + + // If it's a touch-enabled device, mouseenter/leave are fired as + // part of the mouse compatibility events on first tap - the carousel + // would stop cycling until user tapped out of it; + // here, we listen for touchend, explicitly pause the carousel + // (as if it's the second time we tap on it, mouseenter compat event + // is NOT fired) and after a timeout (to allow for mouse compatibility + // events to fire) we explicitly restart cycling + + this.pause(); + if (this.touchTimeout) { + clearTimeout(this.touchTimeout); + } + this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval); + }; + const swipeConfig = { + leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)), + rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)), + endCallback: endCallBack + }; + this._swipeHelper = new Swipe(this._element, swipeConfig); + } + + _keydown(event) { + if (/input|textarea/i.test(event.target.tagName)) { + return; + } + const direction = KEY_TO_DIRECTION[event.key]; + if (direction) { + event.preventDefault(); + this._slide(this._directionToOrder(direction)); + } + } + + _getItemIndex(element) { + return this._getItems().indexOf(element); + } + + _setActiveIndicatorElement(index) { + if (!this._indicatorsElement) { + return; + } + const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement); + activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2); + activeIndicator.removeAttribute('aria-current'); + const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement); + if (newActiveIndicator) { + newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2); + newActiveIndicator.setAttribute('aria-current', 'true'); + } + } + + _updateInterval() { + const element = this._activeElement || this._getActive(); + if (!element) { + return; + } + const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10); + this._config.interval = elementInterval || this._config.defaultInterval; + } + + _slide(order, element = null) { + if (this._isSliding) { + return; + } + const activeElement = this._getActive(); + const isNext = order === ORDER_NEXT; + const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap); + if (nextElement === activeElement) { + return; + } + const nextElementIndex = this._getItemIndex(nextElement); + const triggerEvent = eventName => { + return EventHandler.trigger(this._element, eventName, { + relatedTarget: nextElement, + direction: this._orderToDirection(order), + from: this._getItemIndex(activeElement), + to: nextElementIndex + }); + }; + const slideEvent = triggerEvent(EVENT_SLIDE); + if (slideEvent.defaultPrevented) { + return; + } + if (!activeElement || !nextElement) { + // Some weirdness is happening, so we bail + // TODO: change tests that use empty divs to avoid this check + return; + } + const isCycling = Boolean(this._interval); + this.pause(); + this._isSliding = true; + this._setActiveIndicatorElement(nextElementIndex); + this._activeElement = nextElement; + const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END; + const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV; + nextElement.classList.add(orderClassName); + reflow(nextElement); + activeElement.classList.add(directionalClassName); + nextElement.classList.add(directionalClassName); + const completeCallBack = () => { + nextElement.classList.remove(directionalClassName, orderClassName); + nextElement.classList.add(CLASS_NAME_ACTIVE$2); + activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName); + this._isSliding = false; + triggerEvent(EVENT_SLID); + }; + this._queueCallback(completeCallBack, activeElement, this._isAnimated()); + if (isCycling) { + this.cycle(); + } + } + + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_SLIDE); + } + + _getActive() { + return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element); + } + + _getItems() { + return SelectorEngine.find(SELECTOR_ITEM, this._element); + } + + _clearInterval() { + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + } + + _directionToOrder(direction) { + if (isRTL()) { + return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT; + } + return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV; + } + + _orderToDirection(order) { + if (isRTL()) { + return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT; + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Carousel.getOrCreateInstance(this, config); + if (typeof config === 'number') { + data.to(config); + return; + } + if (typeof config === 'string') { + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { + return; + } + event.preventDefault(); + const carousel = Carousel.getOrCreateInstance(target); + const slideIndex = this.getAttribute('data-bs-slide-to'); + if (slideIndex) { + carousel.to(slideIndex); + carousel._maybeEnableCycle(); + return; + } + if (Manipulator.getDataAttribute(this, 'slide') === 'next') { + carousel.next(); + carousel._maybeEnableCycle(); + return; + } + carousel.prev(); + carousel._maybeEnableCycle(); + }); + EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => { + const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE); + for (const carousel of carousels) { + Carousel.getOrCreateInstance(carousel); + } + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Carousel); + + /** + * -------------------------------------------------------------------------- + * Bootstrap collapse.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$b = 'collapse'; + const DATA_KEY$7 = 'bs.collapse'; + const EVENT_KEY$7 = `.${DATA_KEY$7}`; + const DATA_API_KEY$4 = '.data-api'; + const EVENT_SHOW$6 = `show${EVENT_KEY$7}`; + const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`; + const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`; + const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`; + const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`; + const CLASS_NAME_SHOW$7 = 'show'; + const CLASS_NAME_COLLAPSE = 'collapse'; + const CLASS_NAME_COLLAPSING = 'collapsing'; + const CLASS_NAME_COLLAPSED = 'collapsed'; + const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`; + const CLASS_NAME_HORIZONTAL = 'collapse-horizontal'; + const WIDTH = 'width'; + const HEIGHT = 'height'; + const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'; + const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle="collapse"]'; + const Default$a = { + parent: null, + toggle: true + }; + const DefaultType$a = { + parent: '(null|element)', + toggle: 'boolean' + }; + + /** + * Class definition + */ + + class Collapse extends BaseComponent { + constructor(element, config) { + super(element, config); + this._isTransitioning = false; + this._triggerArray = []; + const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4); + for (const elem of toggleList) { + const selector = SelectorEngine.getSelectorFromElement(elem); + const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element); + if (selector !== null && filterElement.length) { + this._triggerArray.push(elem); + } + } + this._initializeChildren(); + if (!this._config.parent) { + this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()); + } + if (this._config.toggle) { + this.toggle(); + } + } + + // Getters + static get Default() { + return Default$a; + } + + static get DefaultType() { + return DefaultType$a; + } + + static get NAME() { + return NAME$b; + } + + // Public + toggle() { + if (this._isShown()) { + this.hide(); + } else { + this.show(); + } + } + + show() { + if (this._isTransitioning || this._isShown()) { + return; + } + let activeChildren = []; + + // find active children + if (this._config.parent) { + activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, { + toggle: false + })); + } + if (activeChildren.length && activeChildren[0]._isTransitioning) { + return; + } + const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6); + if (startEvent.defaultPrevented) { + return; + } + for (const activeInstance of activeChildren) { + activeInstance.hide(); + } + const dimension = this._getDimension(); + this._element.classList.remove(CLASS_NAME_COLLAPSE); + this._element.classList.add(CLASS_NAME_COLLAPSING); + this._element.style[dimension] = 0; + this._addAriaAndCollapsedClass(this._triggerArray, true); + this._isTransitioning = true; + const complete = () => { + this._isTransitioning = false; + this._element.classList.remove(CLASS_NAME_COLLAPSING); + this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + this._element.style[dimension] = ''; + EventHandler.trigger(this._element, EVENT_SHOWN$6); + }; + const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); + const scrollSize = `scroll${capitalizedDimension}`; + this._queueCallback(complete, this._element, true); + this._element.style[dimension] = `${this._element[scrollSize]}px`; + } + + hide() { + if (this._isTransitioning || !this._isShown()) { + return; + } + const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6); + if (startEvent.defaultPrevented) { + return; + } + const dimension = this._getDimension(); + this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`; + reflow(this._element); + this._element.classList.add(CLASS_NAME_COLLAPSING); + this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + for (const trigger of this._triggerArray) { + const element = SelectorEngine.getElementFromSelector(trigger); + if (element && !this._isShown(element)) { + this._addAriaAndCollapsedClass([trigger], false); + } + } + this._isTransitioning = true; + const complete = () => { + this._isTransitioning = false; + this._element.classList.remove(CLASS_NAME_COLLAPSING); + this._element.classList.add(CLASS_NAME_COLLAPSE); + EventHandler.trigger(this._element, EVENT_HIDDEN$6); + }; + this._element.style[dimension] = ''; + this._queueCallback(complete, this._element, true); + } + + _isShown(element = this._element) { + return element.classList.contains(CLASS_NAME_SHOW$7); + } + + // Private + _configAfterMerge(config) { + config.toggle = Boolean(config.toggle); // Coerce string values + config.parent = getElement(config.parent); + return config; + } + + _getDimension() { + return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT; + } + + _initializeChildren() { + if (!this._config.parent) { + return; + } + const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4); + for (const element of children) { + const selected = SelectorEngine.getElementFromSelector(element); + if (selected) { + this._addAriaAndCollapsedClass([element], this._isShown(selected)); + } + } + } + + _getFirstLevelChildren(selector) { + const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); + // remove children if greater depth + return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element)); + } + + _addAriaAndCollapsedClass(triggerArray, isOpen) { + if (!triggerArray.length) { + return; + } + for (const element of triggerArray) { + element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen); + element.setAttribute('aria-expanded', isOpen); + } + } + + // Static + static jQueryInterface(config) { + const _config = {}; + if (typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false; + } + return this.each(function () { + const data = Collapse.getOrCreateInstance(this, _config); + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) { + // preventDefault only for elements (which change the URL) not inside the collapsible element + if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') { + event.preventDefault(); + } + for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) { + Collapse.getOrCreateInstance(element, { + toggle: false + }).toggle(); + } + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Collapse); + + var top = 'top'; + var bottom = 'bottom'; + var right = 'right'; + var left = 'left'; + var auto = 'auto'; + var basePlacements = [top, bottom, right, left]; + var start = 'start'; + var end = 'end'; + var clippingParents = 'clippingParents'; + var viewport = 'viewport'; + var popper = 'popper'; + var reference = 'reference'; + var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) { + return acc.concat([placement + "-" + start, placement + "-" + end]); + }, []); + var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) { + return acc.concat([placement, placement + "-" + start, placement + "-" + end]); + }, []); // modifiers that need to read the DOM + + var beforeRead = 'beforeRead'; + var read = 'read'; + var afterRead = 'afterRead'; // pure-logic modifiers + + var beforeMain = 'beforeMain'; + var main = 'main'; + var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state) + + var beforeWrite = 'beforeWrite'; + var write = 'write'; + var afterWrite = 'afterWrite'; + var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite]; + + function getNodeName(element) { + return element ? (element.nodeName || '').toLowerCase() : null; + } + + function getWindow(node) { + if (node == null) { + return window; + } + + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + return ownerDocument ? ownerDocument.defaultView || window : window; + } + + return node; + } + + function isElement(node) { + var OwnElement = getWindow(node).Element; + return node instanceof OwnElement || node instanceof Element; + } + + function isHTMLElement(node) { + var OwnElement = getWindow(node).HTMLElement; + return node instanceof OwnElement || node instanceof HTMLElement; + } + + function isShadowRoot(node) { + // IE 11 has no ShadowRoot + if (typeof ShadowRoot === 'undefined') { + return false; + } + + var OwnElement = getWindow(node).ShadowRoot; + return node instanceof OwnElement || node instanceof ShadowRoot; + } + + // and applies them to the HTMLElements such as popper and arrow + + function applyStyles(_ref) { + var state = _ref.state; + Object.keys(state.elements).forEach(function (name) { + var style = state.styles[name] || {}; + var attributes = state.attributes[name] || {}; + var element = state.elements[name]; // arrow is optional + virtual elements + + if (!isHTMLElement(element) || !getNodeName(element)) { + return; + } // Flow doesn't support to extend this property, but it's the most + // effective way to apply styles to an HTMLElement + // $FlowFixMe[cannot-write] + + + Object.assign(element.style, style); + Object.keys(attributes).forEach(function (name) { + var value = attributes[name]; + + if (value === false) { + element.removeAttribute(name); + } else { + element.setAttribute(name, value === true ? '' : value); + } + }); + }); + } + + function effect$2(_ref2) { + var state = _ref2.state; + var initialStyles = { + popper: { + position: state.options.strategy, + left: '0', + top: '0', + margin: '0' + }, + arrow: { + position: 'absolute' + }, + reference: {} + }; + Object.assign(state.elements.popper.style, initialStyles.popper); + state.styles = initialStyles; + + if (state.elements.arrow) { + Object.assign(state.elements.arrow.style, initialStyles.arrow); + } + + return function () { + Object.keys(state.elements).forEach(function (name) { + var element = state.elements[name]; + var attributes = state.attributes[name] || {}; + var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them + + var style = styleProperties.reduce(function (style, property) { + style[property] = ''; + return style; + }, {}); // arrow is optional + virtual elements + + if (!isHTMLElement(element) || !getNodeName(element)) { + return; + } + + Object.assign(element.style, style); + Object.keys(attributes).forEach(function (attribute) { + element.removeAttribute(attribute); + }); + }); + }; + } // eslint-disable-next-line import/no-unused-modules + + + const applyStyles$1 = { + name: 'applyStyles', + enabled: true, + phase: 'write', + fn: applyStyles, + effect: effect$2, + requires: ['computeStyles'] + }; + + function getBasePlacement(placement) { + return placement.split('-')[0]; + } + + var max = Math.max; + var min = Math.min; + var round = Math.round; + + function getUAString() { + var uaData = navigator.userAgentData; + + if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) { + return uaData.brands.map(function (item) { + return item.brand + "/" + item.version; + }).join(' '); + } + + return navigator.userAgent; + } + + function isLayoutViewport() { + return !/^((?!chrome|android).)*safari/i.test(getUAString()); + } + + function getBoundingClientRect(element, includeScale, isFixedStrategy) { + if (includeScale === void 0) { + includeScale = false; + } + + if (isFixedStrategy === void 0) { + isFixedStrategy = false; + } + + var clientRect = element.getBoundingClientRect(); + var scaleX = 1; + var scaleY = 1; + + if (includeScale && isHTMLElement(element)) { + scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1; + scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1; + } + + var _ref = isElement(element) ? getWindow(element) : window, + visualViewport = _ref.visualViewport; + + var addVisualOffsets = !isLayoutViewport() && isFixedStrategy; + var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX; + var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY; + var width = clientRect.width / scaleX; + var height = clientRect.height / scaleY; + return { + width: width, + height: height, + top: y, + right: x + width, + bottom: y + height, + left: x, + x: x, + y: y + }; + } + + // means it doesn't take into account transforms. + + function getLayoutRect(element) { + var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed. + // Fixes https://github.com/popperjs/popper-core/issues/1223 + + var width = element.offsetWidth; + var height = element.offsetHeight; + + if (Math.abs(clientRect.width - width) <= 1) { + width = clientRect.width; + } + + if (Math.abs(clientRect.height - height) <= 1) { + height = clientRect.height; + } + + return { + x: element.offsetLeft, + y: element.offsetTop, + width: width, + height: height + }; + } + + function contains(parent, child) { + var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method + + if (parent.contains(child)) { + return true; + } // then fallback to custom implementation with Shadow DOM support + else if (rootNode && isShadowRoot(rootNode)) { + var next = child; + + do { + if (next && parent.isSameNode(next)) { + return true; + } // $FlowFixMe[prop-missing]: need a better way to handle this... + + + next = next.parentNode || next.host; + } while (next); + } // Give up, the result is false + + + return false; + } + + function getComputedStyle$1(element) { + return getWindow(element).getComputedStyle(element); + } + + function isTableElement(element) { + return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0; + } + + function getDocumentElement(element) { + // $FlowFixMe[incompatible-return]: assume body is always available + return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing] + element.document) || window.document).documentElement; + } + + function getParentNode(element) { + if (getNodeName(element) === 'html') { + return element; + } + + return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle + // $FlowFixMe[incompatible-return] + // $FlowFixMe[prop-missing] + element.assignedSlot || // step into the shadow DOM of the parent of a slotted node + element.parentNode || ( // DOM Element detected + isShadowRoot(element) ? element.host : null) || // ShadowRoot detected + // $FlowFixMe[incompatible-call]: HTMLElement is a Node + getDocumentElement(element) // fallback + + ); + } + + function getTrueOffsetParent(element) { + if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837 + getComputedStyle$1(element).position === 'fixed') { + return null; + } + + return element.offsetParent; + } // `.offsetParent` reports `null` for fixed elements, while absolute elements + // return the containing block + + + function getContainingBlock(element) { + var isFirefox = /firefox/i.test(getUAString()); + var isIE = /Trident/i.test(getUAString()); + + if (isIE && isHTMLElement(element)) { + // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport + var elementCss = getComputedStyle$1(element); + + if (elementCss.position === 'fixed') { + return null; + } + } + + var currentNode = getParentNode(element); + + if (isShadowRoot(currentNode)) { + currentNode = currentNode.host; + } + + while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) { + var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that + // create a containing block. + // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block + + if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') { + return currentNode; + } else { + currentNode = currentNode.parentNode; + } + } + + return null; + } // Gets the closest ancestor positioned element. Handles some edge cases, + // such as table ancestors and cross browser bugs. + + + function getOffsetParent(element) { + var window = getWindow(element); + var offsetParent = getTrueOffsetParent(element); + + while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') { + offsetParent = getTrueOffsetParent(offsetParent); + } + + if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static')) { + return window; + } + + return offsetParent || getContainingBlock(element) || window; + } + + function getMainAxisFromPlacement(placement) { + return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y'; + } + + function within(min$1, value, max$1) { + return max(min$1, min(value, max$1)); + } + + function withinMaxClamp(min, value, max) { + var v = within(min, value, max); + return v > max ? max : v; + } + + function getFreshSideObject() { + return { + top: 0, + right: 0, + bottom: 0, + left: 0 + }; + } + + function mergePaddingObject(paddingObject) { + return Object.assign({}, getFreshSideObject(), paddingObject); + } + + function expandToHashMap(value, keys) { + return keys.reduce(function (hashMap, key) { + hashMap[key] = value; + return hashMap; + }, {}); + } + + var toPaddingObject = function toPaddingObject(padding, state) { + padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, { + placement: state.placement + })) : padding; + return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); + }; + + function arrow(_ref) { + var _state$modifiersData$; + + var state = _ref.state, + name = _ref.name, + options = _ref.options; + var arrowElement = state.elements.arrow; + var popperOffsets = state.modifiersData.popperOffsets; + var basePlacement = getBasePlacement(state.placement); + var axis = getMainAxisFromPlacement(basePlacement); + var isVertical = [left, right].indexOf(basePlacement) >= 0; + var len = isVertical ? 'height' : 'width'; + + if (!arrowElement || !popperOffsets) { + return; + } + + var paddingObject = toPaddingObject(options.padding, state); + var arrowRect = getLayoutRect(arrowElement); + var minProp = axis === 'y' ? top : left; + var maxProp = axis === 'y' ? bottom : right; + var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len]; + var startDiff = popperOffsets[axis] - state.rects.reference[axis]; + var arrowOffsetParent = getOffsetParent(arrowElement); + var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0; + var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is + // outside of the popper bounds + + var min = paddingObject[minProp]; + var max = clientSize - arrowRect[len] - paddingObject[maxProp]; + var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference; + var offset = within(min, center, max); // Prevents breaking syntax highlighting... + + var axisProp = axis; + state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$); + } + + function effect$1(_ref2) { + var state = _ref2.state, + options = _ref2.options; + var _options$element = options.element, + arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element; + + if (arrowElement == null) { + return; + } // CSS selector + + + if (typeof arrowElement === 'string') { + arrowElement = state.elements.popper.querySelector(arrowElement); + + if (!arrowElement) { + return; + } + } + + if (!contains(state.elements.popper, arrowElement)) { + return; + } + + state.elements.arrow = arrowElement; + } // eslint-disable-next-line import/no-unused-modules + + + const arrow$1 = { + name: 'arrow', + enabled: true, + phase: 'main', + fn: arrow, + effect: effect$1, + requires: ['popperOffsets'], + requiresIfExists: ['preventOverflow'] + }; + + function getVariation(placement) { + return placement.split('-')[1]; + } + + var unsetSides = { + top: 'auto', + right: 'auto', + bottom: 'auto', + left: 'auto' + }; // Round the offsets to the nearest suitable subpixel based on the DPR. + // Zooming can change the DPR, but it seems to report a value that will + // cleanly divide the values into the appropriate subpixels. + + function roundOffsetsByDPR(_ref, win) { + var x = _ref.x, + y = _ref.y; + var dpr = win.devicePixelRatio || 1; + return { + x: round(x * dpr) / dpr || 0, + y: round(y * dpr) / dpr || 0 + }; + } + + function mapToStyles(_ref2) { + var _Object$assign2; + + var popper = _ref2.popper, + popperRect = _ref2.popperRect, + placement = _ref2.placement, + variation = _ref2.variation, + offsets = _ref2.offsets, + position = _ref2.position, + gpuAcceleration = _ref2.gpuAcceleration, + adaptive = _ref2.adaptive, + roundOffsets = _ref2.roundOffsets, + isFixed = _ref2.isFixed; + var _offsets$x = offsets.x, + x = _offsets$x === void 0 ? 0 : _offsets$x, + _offsets$y = offsets.y, + y = _offsets$y === void 0 ? 0 : _offsets$y; + + var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({ + x: x, + y: y + }) : { + x: x, + y: y + }; + + x = _ref3.x; + y = _ref3.y; + var hasX = offsets.hasOwnProperty('x'); + var hasY = offsets.hasOwnProperty('y'); + var sideX = left; + var sideY = top; + var win = window; + + if (adaptive) { + var offsetParent = getOffsetParent(popper); + var heightProp = 'clientHeight'; + var widthProp = 'clientWidth'; + + if (offsetParent === getWindow(popper)) { + offsetParent = getDocumentElement(popper); + + if (getComputedStyle$1(offsetParent).position !== 'static' && position === 'absolute') { + heightProp = 'scrollHeight'; + widthProp = 'scrollWidth'; + } + } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it + + + offsetParent = offsetParent; + + if (placement === top || (placement === left || placement === right) && variation === end) { + sideY = bottom; + var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing] + offsetParent[heightProp]; + y -= offsetY - popperRect.height; + y *= gpuAcceleration ? 1 : -1; + } + + if (placement === left || (placement === top || placement === bottom) && variation === end) { + sideX = right; + var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing] + offsetParent[widthProp]; + x -= offsetX - popperRect.width; + x *= gpuAcceleration ? 1 : -1; + } + } + + var commonStyles = Object.assign({ + position: position + }, adaptive && unsetSides); + + var _ref4 = roundOffsets === true ? roundOffsetsByDPR({ + x: x, + y: y + }, getWindow(popper)) : { + x: x, + y: y + }; + + x = _ref4.x; + y = _ref4.y; + + if (gpuAcceleration) { + var _Object$assign; + + return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign)); + } + + return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2)); + } + + function computeStyles(_ref5) { + var state = _ref5.state, + options = _ref5.options; + var _options$gpuAccelerat = options.gpuAcceleration, + gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat, + _options$adaptive = options.adaptive, + adaptive = _options$adaptive === void 0 ? true : _options$adaptive, + _options$roundOffsets = options.roundOffsets, + roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets; + var commonStyles = { + placement: getBasePlacement(state.placement), + variation: getVariation(state.placement), + popper: state.elements.popper, + popperRect: state.rects.popper, + gpuAcceleration: gpuAcceleration, + isFixed: state.options.strategy === 'fixed' + }; + + if (state.modifiersData.popperOffsets != null) { + state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, { + offsets: state.modifiersData.popperOffsets, + position: state.options.strategy, + adaptive: adaptive, + roundOffsets: roundOffsets + }))); + } + + if (state.modifiersData.arrow != null) { + state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, { + offsets: state.modifiersData.arrow, + position: 'absolute', + adaptive: false, + roundOffsets: roundOffsets + }))); + } + + state.attributes.popper = Object.assign({}, state.attributes.popper, { + 'data-popper-placement': state.placement + }); + } // eslint-disable-next-line import/no-unused-modules + + + const computeStyles$1 = { + name: 'computeStyles', + enabled: true, + phase: 'beforeWrite', + fn: computeStyles, + data: {} + }; + + var passive = { + passive: true + }; + + function effect(_ref) { + var state = _ref.state, + instance = _ref.instance, + options = _ref.options; + var _options$scroll = options.scroll, + scroll = _options$scroll === void 0 ? true : _options$scroll, + _options$resize = options.resize, + resize = _options$resize === void 0 ? true : _options$resize; + var window = getWindow(state.elements.popper); + var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper); + + if (scroll) { + scrollParents.forEach(function (scrollParent) { + scrollParent.addEventListener('scroll', instance.update, passive); + }); + } + + if (resize) { + window.addEventListener('resize', instance.update, passive); + } + + return function () { + if (scroll) { + scrollParents.forEach(function (scrollParent) { + scrollParent.removeEventListener('scroll', instance.update, passive); + }); + } + + if (resize) { + window.removeEventListener('resize', instance.update, passive); + } + }; + } // eslint-disable-next-line import/no-unused-modules + + + const eventListeners = { + name: 'eventListeners', + enabled: true, + phase: 'write', + fn: function fn() { + }, + effect: effect, + data: {} + }; + + var hash$1 = { + left: 'right', + right: 'left', + bottom: 'top', + top: 'bottom' + }; + + function getOppositePlacement(placement) { + return placement.replace(/left|right|bottom|top/g, function (matched) { + return hash$1[matched]; + }); + } + + var hash = { + start: 'end', + end: 'start' + }; + + function getOppositeVariationPlacement(placement) { + return placement.replace(/start|end/g, function (matched) { + return hash[matched]; + }); + } + + function getWindowScroll(node) { + var win = getWindow(node); + var scrollLeft = win.pageXOffset; + var scrollTop = win.pageYOffset; + return { + scrollLeft: scrollLeft, + scrollTop: scrollTop + }; + } + + function getWindowScrollBarX(element) { + // If has a CSS width greater than the viewport, then this will be + // incorrect for RTL. + // Popper 1 is broken in this case and never had a bug report so let's assume + // it's not an issue. I don't think anyone ever specifies width on + // anyway. + // Browsers where the left scrollbar doesn't cause an issue report `0` for + // this (e.g. Edge 2019, IE11, Safari) + return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft; + } + + function getViewportRect(element, strategy) { + var win = getWindow(element); + var html = getDocumentElement(element); + var visualViewport = win.visualViewport; + var width = html.clientWidth; + var height = html.clientHeight; + var x = 0; + var y = 0; + + if (visualViewport) { + width = visualViewport.width; + height = visualViewport.height; + var layoutViewport = isLayoutViewport(); + + if (layoutViewport || !layoutViewport && strategy === 'fixed') { + x = visualViewport.offsetLeft; + y = visualViewport.offsetTop; + } + } + + return { + width: width, + height: height, + x: x + getWindowScrollBarX(element), + y: y + }; + } + + // of the `` and `` rect bounds if horizontally scrollable + + function getDocumentRect(element) { + var _element$ownerDocumen; + + var html = getDocumentElement(element); + var winScroll = getWindowScroll(element); + var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body; + var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0); + var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0); + var x = -winScroll.scrollLeft + getWindowScrollBarX(element); + var y = -winScroll.scrollTop; + + if (getComputedStyle$1(body || html).direction === 'rtl') { + x += max(html.clientWidth, body ? body.clientWidth : 0) - width; + } + + return { + width: width, + height: height, + x: x, + y: y + }; + } + + function isScrollParent(element) { + // Firefox wants us to check `-x` and `-y` variations as well + var _getComputedStyle = getComputedStyle$1(element), + overflow = _getComputedStyle.overflow, + overflowX = _getComputedStyle.overflowX, + overflowY = _getComputedStyle.overflowY; + + return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX); } - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/index.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - const MAX_UID = 1000000; - const MILLISECONDS_MULTIPLIER = 1000; - const TRANSITION_END = 'transitionend'; - - /** - * Properly escape IDs selectors to handle weird IDs - * @param {string} selector - * @returns {string} - */ - const parseSelector = selector => { - if (selector && window.CSS && window.CSS.escape) { - // document.querySelector needs escaping to handle IDs (html5+) containing for instance / - selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); + + function getScrollParent(node) { + if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) { + // $FlowFixMe[incompatible-return]: assume body is always available + return node.ownerDocument.body; + } + + if (isHTMLElement(node) && isScrollParent(node)) { + return node; + } + + return getScrollParent(getParentNode(node)); + } + + /* + given a DOM element, return the list of all scroll parents, up the list of ancesors + until we get to the top window object. This list is what we attach scroll listeners + to, because if any of these parent elements scroll, we'll need to re-calculate the + reference element's position. + */ + + function listScrollParents(element, list) { + var _element$ownerDocumen; + + if (list === void 0) { + list = []; + } + + var scrollParent = getScrollParent(element); + var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body); + var win = getWindow(scrollParent); + var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent; + var updatedList = list.concat(target); + return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here + updatedList.concat(listScrollParents(getParentNode(target))); + } + + function rectToClientRect(rect) { + return Object.assign({}, rect, { + left: rect.x, + top: rect.y, + right: rect.x + rect.width, + bottom: rect.y + rect.height + }); + } + + function getInnerBoundingClientRect(element, strategy) { + var rect = getBoundingClientRect(element, false, strategy === 'fixed'); + rect.top = rect.top + element.clientTop; + rect.left = rect.left + element.clientLeft; + rect.bottom = rect.top + element.clientHeight; + rect.right = rect.left + element.clientWidth; + rect.width = element.clientWidth; + rect.height = element.clientHeight; + rect.x = rect.left; + rect.y = rect.top; + return rect; + } + + function getClientRectFromMixedType(element, clippingParent, strategy) { + return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element))); + } // A "clipping parent" is an overflowable container with the characteristic of + // clipping (or hiding) overflowing elements with a position different from + // `initial` + + + function getClippingParents(element) { + var clippingParents = listScrollParents(getParentNode(element)); + var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle$1(element).position) >= 0; + var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element; + + if (!isElement(clipperElement)) { + return []; + } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414 + + + return clippingParents.filter(function (clippingParent) { + return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body'; + }); + } // Gets the maximum area that the element is visible in due to any number of + // clipping parents + + + function getClippingRect(element, boundary, rootBoundary, strategy) { + var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary); + var clippingParents = [].concat(mainClippingParents, [rootBoundary]); + var firstClippingParent = clippingParents[0]; + var clippingRect = clippingParents.reduce(function (accRect, clippingParent) { + var rect = getClientRectFromMixedType(element, clippingParent, strategy); + accRect.top = max(rect.top, accRect.top); + accRect.right = min(rect.right, accRect.right); + accRect.bottom = min(rect.bottom, accRect.bottom); + accRect.left = max(rect.left, accRect.left); + return accRect; + }, getClientRectFromMixedType(element, firstClippingParent, strategy)); + clippingRect.width = clippingRect.right - clippingRect.left; + clippingRect.height = clippingRect.bottom - clippingRect.top; + clippingRect.x = clippingRect.left; + clippingRect.y = clippingRect.top; + return clippingRect; + } + + function computeOffsets(_ref) { + var reference = _ref.reference, + element = _ref.element, + placement = _ref.placement; + var basePlacement = placement ? getBasePlacement(placement) : null; + var variation = placement ? getVariation(placement) : null; + var commonX = reference.x + reference.width / 2 - element.width / 2; + var commonY = reference.y + reference.height / 2 - element.height / 2; + var offsets; + + switch (basePlacement) { + case top: + offsets = { + x: commonX, + y: reference.y - element.height + }; + break; + + case bottom: + offsets = { + x: commonX, + y: reference.y + reference.height + }; + break; + + case right: + offsets = { + x: reference.x + reference.width, + y: commonY + }; + break; + + case left: + offsets = { + x: reference.x - element.width, + y: commonY + }; + break; + + default: + offsets = { + x: reference.x, + y: reference.y + }; + } + + var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null; + + if (mainAxis != null) { + var len = mainAxis === 'y' ? 'height' : 'width'; + + switch (variation) { + case start: + offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2); + break; + + case end: + offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2); + break; + } + } + + return offsets; + } + + function detectOverflow(state, options) { + if (options === void 0) { + options = {}; + } + + var _options = options, + _options$placement = _options.placement, + placement = _options$placement === void 0 ? state.placement : _options$placement, + _options$strategy = _options.strategy, + strategy = _options$strategy === void 0 ? state.strategy : _options$strategy, + _options$boundary = _options.boundary, + boundary = _options$boundary === void 0 ? clippingParents : _options$boundary, + _options$rootBoundary = _options.rootBoundary, + rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary, + _options$elementConte = _options.elementContext, + elementContext = _options$elementConte === void 0 ? popper : _options$elementConte, + _options$altBoundary = _options.altBoundary, + altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary, + _options$padding = _options.padding, + padding = _options$padding === void 0 ? 0 : _options$padding; + var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); + var altContext = elementContext === popper ? reference : popper; + var popperRect = state.rects.popper; + var element = state.elements[altBoundary ? altContext : elementContext]; + var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy); + var referenceClientRect = getBoundingClientRect(state.elements.reference); + var popperOffsets = computeOffsets({ + reference: referenceClientRect, + element: popperRect, + strategy: 'absolute', + placement: placement + }); + var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets)); + var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect + // 0 or negative = within the clipping rect + + var overflowOffsets = { + top: clippingClientRect.top - elementClientRect.top + paddingObject.top, + bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom, + left: clippingClientRect.left - elementClientRect.left + paddingObject.left, + right: elementClientRect.right - clippingClientRect.right + paddingObject.right + }; + var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element + + if (elementContext === popper && offsetData) { + var offset = offsetData[placement]; + Object.keys(overflowOffsets).forEach(function (key) { + var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1; + var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x'; + overflowOffsets[key] += offset[axis] * multiply; + }); + } + + return overflowOffsets; + } + + function computeAutoPlacement(state, options) { + if (options === void 0) { + options = {}; + } + + var _options = options, + placement = _options.placement, + boundary = _options.boundary, + rootBoundary = _options.rootBoundary, + padding = _options.padding, + flipVariations = _options.flipVariations, + _options$allowedAutoP = _options.allowedAutoPlacements, + allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP; + var variation = getVariation(placement); + var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) { + return getVariation(placement) === variation; + }) : basePlacements; + var allowedPlacements = placements$1.filter(function (placement) { + return allowedAutoPlacements.indexOf(placement) >= 0; + }); + + if (allowedPlacements.length === 0) { + allowedPlacements = placements$1; + } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions... + + + var overflows = allowedPlacements.reduce(function (acc, placement) { + acc[placement] = detectOverflow(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding + })[getBasePlacement(placement)]; + return acc; + }, {}); + return Object.keys(overflows).sort(function (a, b) { + return overflows[a] - overflows[b]; + }); + } + + function getExpandedFallbackPlacements(placement) { + if (getBasePlacement(placement) === auto) { + return []; + } + + var oppositePlacement = getOppositePlacement(placement); + return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)]; + } + + function flip(_ref) { + var state = _ref.state, + options = _ref.options, + name = _ref.name; + + if (state.modifiersData[name]._skip) { + return; + } + + var _options$mainAxis = options.mainAxis, + checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, + _options$altAxis = options.altAxis, + checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis, + specifiedFallbackPlacements = options.fallbackPlacements, + padding = options.padding, + boundary = options.boundary, + rootBoundary = options.rootBoundary, + altBoundary = options.altBoundary, + _options$flipVariatio = options.flipVariations, + flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio, + allowedAutoPlacements = options.allowedAutoPlacements; + var preferredPlacement = state.options.placement; + var basePlacement = getBasePlacement(preferredPlacement); + var isBasePlacement = basePlacement === preferredPlacement; + var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement)); + var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) { + return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding, + flipVariations: flipVariations, + allowedAutoPlacements: allowedAutoPlacements + }) : placement); + }, []); + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var checksMap = new Map(); + var makeFallbackChecks = true; + var firstFittingPlacement = placements[0]; + + for (var i = 0; i < placements.length; i++) { + var placement = placements[i]; + + var _basePlacement = getBasePlacement(placement); + + var isStartVariation = getVariation(placement) === start; + var isVertical = [top, bottom].indexOf(_basePlacement) >= 0; + var len = isVertical ? 'width' : 'height'; + var overflow = detectOverflow(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + altBoundary: altBoundary, + padding: padding + }); + var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top; + + if (referenceRect[len] > popperRect[len]) { + mainVariationSide = getOppositePlacement(mainVariationSide); + } + + var altVariationSide = getOppositePlacement(mainVariationSide); + var checks = []; + + if (checkMainAxis) { + checks.push(overflow[_basePlacement] <= 0); + } + + if (checkAltAxis) { + checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0); + } + + if (checks.every(function (check) { + return check; + })) { + firstFittingPlacement = placement; + makeFallbackChecks = false; + break; + } + + checksMap.set(placement, checks); + } + + if (makeFallbackChecks) { + // `2` may be desired in some cases – research later + var numberOfChecks = flipVariations ? 3 : 1; + + var _loop = function _loop(_i) { + var fittingPlacement = placements.find(function (placement) { + var checks = checksMap.get(placement); + + if (checks) { + return checks.slice(0, _i).every(function (check) { + return check; + }); + } + }); + + if (fittingPlacement) { + firstFittingPlacement = fittingPlacement; + return "break"; + } + }; + + for (var _i = numberOfChecks; _i > 0; _i--) { + var _ret = _loop(_i); + + if (_ret === "break") break; + } + } + + if (state.placement !== firstFittingPlacement) { + state.modifiersData[name]._skip = true; + state.placement = firstFittingPlacement; + state.reset = true; + } + } // eslint-disable-next-line import/no-unused-modules + + + const flip$1 = { + name: 'flip', + enabled: true, + phase: 'main', + fn: flip, + requiresIfExists: ['offset'], + data: { + _skip: false + } + }; + + function getSideOffsets(overflow, rect, preventedOffsets) { + if (preventedOffsets === void 0) { + preventedOffsets = { + x: 0, + y: 0 + }; + } + + return { + top: overflow.top - rect.height - preventedOffsets.y, + right: overflow.right - rect.width + preventedOffsets.x, + bottom: overflow.bottom - rect.height + preventedOffsets.y, + left: overflow.left - rect.width - preventedOffsets.x + }; + } + + function isAnySideFullyClipped(overflow) { + return [top, right, bottom, left].some(function (side) { + return overflow[side] >= 0; + }); + } + + function hide(_ref) { + var state = _ref.state, + name = _ref.name; + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var preventedOffsets = state.modifiersData.preventOverflow; + var referenceOverflow = detectOverflow(state, { + elementContext: 'reference' + }); + var popperAltOverflow = detectOverflow(state, { + altBoundary: true + }); + var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect); + var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets); + var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets); + var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets); + state.modifiersData[name] = { + referenceClippingOffsets: referenceClippingOffsets, + popperEscapeOffsets: popperEscapeOffsets, + isReferenceHidden: isReferenceHidden, + hasPopperEscaped: hasPopperEscaped + }; + state.attributes.popper = Object.assign({}, state.attributes.popper, { + 'data-popper-reference-hidden': isReferenceHidden, + 'data-popper-escaped': hasPopperEscaped + }); + } // eslint-disable-next-line import/no-unused-modules + + + const hide$1 = { + name: 'hide', + enabled: true, + phase: 'main', + requiresIfExists: ['preventOverflow'], + fn: hide + }; + + function distanceAndSkiddingToXY(placement, rects, offset) { + var basePlacement = getBasePlacement(placement); + var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1; + + var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, { + placement: placement + })) : offset, + skidding = _ref[0], + distance = _ref[1]; + + skidding = skidding || 0; + distance = (distance || 0) * invertDistance; + return [left, right].indexOf(basePlacement) >= 0 ? { + x: distance, + y: skidding + } : { + x: skidding, + y: distance + }; } - return selector; - }; - // Shout-out Angus Croll (https://goo.gl/pxwQGp) - const toType = object => { - if (object === null || object === undefined) { - return `${object}`; - } - return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); - }; - - /** - * Public Util API - */ - - const getUID = prefix => { - do { - prefix += Math.floor(Math.random() * MAX_UID); - } while (document.getElementById(prefix)); - return prefix; - }; - const getTransitionDurationFromElement = element => { - if (!element) { - return 0; - } + function offset(_ref2) { + var state = _ref2.state, + options = _ref2.options, + name = _ref2.name; + var _options$offset = options.offset, + offset = _options$offset === void 0 ? [0, 0] : _options$offset; + var data = placements.reduce(function (acc, placement) { + acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset); + return acc; + }, {}); + var _data$state$placement = data[state.placement], + x = _data$state$placement.x, + y = _data$state$placement.y; + + if (state.modifiersData.popperOffsets != null) { + state.modifiersData.popperOffsets.x += x; + state.modifiersData.popperOffsets.y += y; + } + + state.modifiersData[name] = data; + } // eslint-disable-next-line import/no-unused-modules + + + const offset$1 = { + name: 'offset', + enabled: true, + phase: 'main', + requires: ['popperOffsets'], + fn: offset + }; + + function popperOffsets(_ref) { + var state = _ref.state, + name = _ref.name; + // Offsets are the actual position the popper needs to have to be + // properly positioned near its reference element + // This is the most basic placement, and will be adjusted by + // the modifiers in the next step + state.modifiersData[name] = computeOffsets({ + reference: state.rects.reference, + element: state.rects.popper, + strategy: 'absolute', + placement: state.placement + }); + } // eslint-disable-next-line import/no-unused-modules + + + const popperOffsets$1 = { + name: 'popperOffsets', + enabled: true, + phase: 'read', + fn: popperOffsets, + data: {} + }; + + function getAltAxis(axis) { + return axis === 'x' ? 'y' : 'x'; + } + + function preventOverflow(_ref) { + var state = _ref.state, + options = _ref.options, + name = _ref.name; + var _options$mainAxis = options.mainAxis, + checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, + _options$altAxis = options.altAxis, + checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis, + boundary = options.boundary, + rootBoundary = options.rootBoundary, + altBoundary = options.altBoundary, + padding = options.padding, + _options$tether = options.tether, + tether = _options$tether === void 0 ? true : _options$tether, + _options$tetherOffset = options.tetherOffset, + tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset; + var overflow = detectOverflow(state, { + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding, + altBoundary: altBoundary + }); + var basePlacement = getBasePlacement(state.placement); + var variation = getVariation(state.placement); + var isBasePlacement = !variation; + var mainAxis = getMainAxisFromPlacement(basePlacement); + var altAxis = getAltAxis(mainAxis); + var popperOffsets = state.modifiersData.popperOffsets; + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, { + placement: state.placement + })) : tetherOffset; + var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? { + mainAxis: tetherOffsetValue, + altAxis: tetherOffsetValue + } : Object.assign({ + mainAxis: 0, + altAxis: 0 + }, tetherOffsetValue); + var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null; + var data = { + x: 0, + y: 0 + }; + + if (!popperOffsets) { + return; + } + + if (checkMainAxis) { + var _offsetModifierState$; + + var mainSide = mainAxis === 'y' ? top : left; + var altSide = mainAxis === 'y' ? bottom : right; + var len = mainAxis === 'y' ? 'height' : 'width'; + var offset = popperOffsets[mainAxis]; + var min$1 = offset + overflow[mainSide]; + var max$1 = offset - overflow[altSide]; + var additive = tether ? -popperRect[len] / 2 : 0; + var minLen = variation === start ? referenceRect[len] : popperRect[len]; + var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go + // outside the reference bounds + + var arrowElement = state.elements.arrow; + var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : { + width: 0, + height: 0 + }; + var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject(); + var arrowPaddingMin = arrowPaddingObject[mainSide]; + var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want + // to include its full size in the calculation. If the reference is small + // and near the edge of a boundary, the popper can overflow even if the + // reference is not overflowing as well (e.g. virtual elements with no + // width or height) + + var arrowLen = within(0, referenceRect[len], arrowRect[len]); + var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis; + var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis; + var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow); + var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0; + var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0; + var tetherMin = offset + minOffset - offsetModifierValue - clientOffset; + var tetherMax = offset + maxOffset - offsetModifierValue; + var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1); + popperOffsets[mainAxis] = preventedOffset; + data[mainAxis] = preventedOffset - offset; + } + + if (checkAltAxis) { + var _offsetModifierState$2; + + var _mainSide = mainAxis === 'x' ? top : left; + + var _altSide = mainAxis === 'x' ? bottom : right; + + var _offset = popperOffsets[altAxis]; + + var _len = altAxis === 'y' ? 'height' : 'width'; - // Get transition-duration of the element - let { - transitionDuration, - transitionDelay - } = window.getComputedStyle(element); - const floatTransitionDuration = Number.parseFloat(transitionDuration); - const floatTransitionDelay = Number.parseFloat(transitionDelay); - - // Return 0 if element or transition duration is not found - if (!floatTransitionDuration && !floatTransitionDelay) { - return 0; - } + var _min = _offset + overflow[_mainSide]; - // If multiple durations are defined, take the first - transitionDuration = transitionDuration.split(',')[0]; - transitionDelay = transitionDelay.split(',')[0]; - return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; - }; - const triggerTransitionEnd = element => { - element.dispatchEvent(new Event(TRANSITION_END)); - }; - const isElement$1 = object => { - if (!object || typeof object !== 'object') { - return false; - } - if (typeof object.jquery !== 'undefined') { - object = object[0]; - } - return typeof object.nodeType !== 'undefined'; - }; - const getElement = object => { - // it's a jQuery object or a node element - if (isElement$1(object)) { - return object.jquery ? object[0] : object; - } - if (typeof object === 'string' && object.length > 0) { - return document.querySelector(parseSelector(object)); - } - return null; - }; - const isVisible = element => { - if (!isElement$1(element) || element.getClientRects().length === 0) { - return false; - } - const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; - // Handle `details` element as its content may falsie appear visible when it is closed - const closedDetails = element.closest('details:not([open])'); - if (!closedDetails) { - return elementIsVisible; - } - if (closedDetails !== element) { - const summary = element.closest('summary'); - if (summary && summary.parentNode !== closedDetails) { - return false; - } - if (summary === null) { - return false; - } - } - return elementIsVisible; - }; - const isDisabled = element => { - if (!element || element.nodeType !== Node.ELEMENT_NODE) { - return true; - } - if (element.classList.contains('disabled')) { - return true; - } - if (typeof element.disabled !== 'undefined') { - return element.disabled; - } - return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; - }; - const findShadowRoot = element => { - if (!document.documentElement.attachShadow) { - return null; - } + var _max = _offset - overflow[_altSide]; - // Can find the shadow root otherwise it'll return the document - if (typeof element.getRootNode === 'function') { - const root = element.getRootNode(); - return root instanceof ShadowRoot ? root : null; - } - if (element instanceof ShadowRoot) { - return element; + var isOriginSide = [top, left].indexOf(basePlacement) !== -1; + + var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0; + + var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis; + + var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max; + + var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max); + + popperOffsets[altAxis] = _preventedOffset; + data[altAxis] = _preventedOffset - _offset; + } + + state.modifiersData[name] = data; + } // eslint-disable-next-line import/no-unused-modules + + + const preventOverflow$1 = { + name: 'preventOverflow', + enabled: true, + phase: 'main', + fn: preventOverflow, + requiresIfExists: ['offset'] + }; + + function getHTMLElementScroll(element) { + return { + scrollLeft: element.scrollLeft, + scrollTop: element.scrollTop + }; } - // when we don't find a shadow root - if (!element.parentNode) { - return null; + function getNodeScroll(node) { + if (node === getWindow(node) || !isHTMLElement(node)) { + return getWindowScroll(node); + } else { + return getHTMLElementScroll(node); + } } - return findShadowRoot(element.parentNode); - }; - const noop = () => {}; - - /** - * Trick to restart an element's animation - * - * @param {HTMLElement} element - * @return void - * - * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation - */ - const reflow = element => { - element.offsetHeight; // eslint-disable-line no-unused-expressions - }; - const getjQuery = () => { - if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { - return window.jQuery; + + function isElementScaled(element) { + var rect = element.getBoundingClientRect(); + var scaleX = round(rect.width) / element.offsetWidth || 1; + var scaleY = round(rect.height) / element.offsetHeight || 1; + return scaleX !== 1 || scaleY !== 1; + } // Returns the composite rect of an element relative to its offsetParent. + // Composite means it takes into account transforms as well as layout. + + + function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) { + if (isFixed === void 0) { + isFixed = false; + } + + var isOffsetParentAnElement = isHTMLElement(offsetParent); + var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent); + var documentElement = getDocumentElement(offsetParent); + var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed); + var scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + var offsets = { + x: 0, + y: 0 + }; + + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078 + isScrollParent(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + + if (isHTMLElement(offsetParent)) { + offsets = getBoundingClientRect(offsetParent, true); + offsets.x += offsetParent.clientLeft; + offsets.y += offsetParent.clientTop; + } else if (documentElement) { + offsets.x = getWindowScrollBarX(documentElement); + } + } + + return { + x: rect.left + scroll.scrollLeft - offsets.x, + y: rect.top + scroll.scrollTop - offsets.y, + width: rect.width, + height: rect.height + }; } - return null; - }; - const DOMContentLoadedCallbacks = []; - const onDOMContentLoaded = callback => { - if (document.readyState === 'loading') { - // add listener on the first call when the document is in loading state - if (!DOMContentLoadedCallbacks.length) { - document.addEventListener('DOMContentLoaded', () => { - for (const callback of DOMContentLoadedCallbacks) { - callback(); - } + + function order(modifiers) { + var map = new Map(); + var visited = new Set(); + var result = []; + modifiers.forEach(function (modifier) { + map.set(modifier.name, modifier); + }); // On visiting object, check for its dependencies and visit them recursively + + function sort(modifier) { + visited.add(modifier.name); + var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []); + requires.forEach(function (dep) { + if (!visited.has(dep)) { + var depModifier = map.get(dep); + + if (depModifier) { + sort(depModifier); + } + } + }); + result.push(modifier); + } + + modifiers.forEach(function (modifier) { + if (!visited.has(modifier.name)) { + // check for visited object + sort(modifier); + } }); - } - DOMContentLoadedCallbacks.push(callback); - } else { - callback(); - } - }; - const isRTL = () => document.documentElement.dir === 'rtl'; - const defineJQueryPlugin = plugin => { - onDOMContentLoaded(() => { - const $ = getjQuery(); - /* istanbul ignore if */ - if ($) { - const name = plugin.NAME; - const JQUERY_NO_CONFLICT = $.fn[name]; - $.fn[name] = plugin.jQueryInterface; - $.fn[name].Constructor = plugin; - $.fn[name].noConflict = () => { - $.fn[name] = JQUERY_NO_CONFLICT; - return plugin.jQueryInterface; + return result; + } + + function orderModifiers(modifiers) { + // order based on dependencies + var orderedModifiers = order(modifiers); // order based on phase + + return modifierPhases.reduce(function (acc, phase) { + return acc.concat(orderedModifiers.filter(function (modifier) { + return modifier.phase === phase; + })); + }, []); + } + + function debounce(fn) { + var pending; + return function () { + if (!pending) { + pending = new Promise(function (resolve) { + Promise.resolve().then(function () { + pending = undefined; + resolve(fn()); + }); + }); + } + + return pending; }; - } - }); - }; - const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { - return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue; - }; - const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { - if (!waitForTransition) { - execute(callback); - return; - } - const durationPadding = 5; - const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; - let called = false; - const handler = ({ - target - }) => { - if (target !== transitionElement) { - return; - } - called = true; - transitionElement.removeEventListener(TRANSITION_END, handler); - execute(callback); - }; - transitionElement.addEventListener(TRANSITION_END, handler); - setTimeout(() => { - if (!called) { - triggerTransitionEnd(transitionElement); - } - }, emulatedDuration); - }; - - /** - * Return the previous/next element of a list. - * - * @param {array} list The list of elements - * @param activeElement The active element - * @param shouldGetNext Choose to get next or previous element - * @param isCycleAllowed - * @return {Element|elem} The proper element - */ - const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { - const listLength = list.length; - let index = list.indexOf(activeElement); - - // if the element does not exist in the list return an element - // depending on the direction and if cycle is allowed - if (index === -1) { - return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; } - index += shouldGetNext ? 1 : -1; - if (isCycleAllowed) { - index = (index + listLength) % listLength; + + function mergeByName(modifiers) { + var merged = modifiers.reduce(function (merged, current) { + var existing = merged[current.name]; + merged[current.name] = existing ? Object.assign({}, existing, current, { + options: Object.assign({}, existing.options, current.options), + data: Object.assign({}, existing.data, current.data) + }) : current; + return merged; + }, {}); // IE11 does not support Object.values + + return Object.keys(merged).map(function (key) { + return merged[key]; + }); } - return list[Math.max(0, Math.min(index, listLength - 1))]; - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/event-handler.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const namespaceRegex = /[^.]*(?=\..*)\.|.*/; - const stripNameRegex = /\..*/; - const stripUidRegex = /::\d+$/; - const eventRegistry = {}; // Events storage - let uidEvent = 1; - const customEvents = { - mouseenter: 'mouseover', - mouseleave: 'mouseout' - }; - const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); - - /** - * Private methods - */ - - function makeEventUid(element, uid) { - return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; - } - function getElementEvents(element) { - const uid = makeEventUid(element); - element.uidEvent = uid; - eventRegistry[uid] = eventRegistry[uid] || {}; - return eventRegistry[uid]; - } - function bootstrapHandler(element, fn) { - return function handler(event) { - hydrateObj(event, { - delegateTarget: element - }); - if (handler.oneOff) { - EventHandler.off(element, event.type, fn); - } - return fn.apply(element, [event]); - }; - } - function bootstrapDelegationHandler(element, selector, fn) { - return function handler(event) { - const domElements = element.querySelectorAll(selector); - for (let { - target - } = event; target && target !== this; target = target.parentNode) { - for (const domElement of domElements) { - if (domElement !== target) { - continue; - } - hydrateObj(event, { - delegateTarget: target - }); - if (handler.oneOff) { - EventHandler.off(element, event.type, selector, fn); - } - return fn.apply(target, [event]); - } - } + + var DEFAULT_OPTIONS = { + placement: 'bottom', + modifiers: [], + strategy: 'absolute' }; - } - function findHandler(events, callable, delegationSelector = null) { - return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); - } - function normalizeParameters(originalTypeEvent, handler, delegationFunction) { - const isDelegated = typeof handler === 'string'; - // TODO: tooltip passes `false` instead of selector, so we need to check - const callable = isDelegated ? delegationFunction : handler || delegationFunction; - let typeEvent = getTypeEvent(originalTypeEvent); - if (!nativeEvents.has(typeEvent)) { - typeEvent = originalTypeEvent; - } - return [isDelegated, callable, typeEvent]; - } - function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { - if (typeof originalTypeEvent !== 'string' || !element) { - return; - } - let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); - - // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position - // this prevents the handler from being dispatched the same way as mouseover or mouseout does - if (originalTypeEvent in customEvents) { - const wrapFunction = fn => { - return function (event) { - if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { - return fn.call(this, event); - } - }; - }; - callable = wrapFunction(callable); - } - const events = getElementEvents(element); - const handlers = events[typeEvent] || (events[typeEvent] = {}); - const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); - if (previousFunction) { - previousFunction.oneOff = previousFunction.oneOff && oneOff; - return; - } - const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); - const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); - fn.delegationSelector = isDelegated ? handler : null; - fn.callable = callable; - fn.oneOff = oneOff; - fn.uidEvent = uid; - handlers[uid] = fn; - element.addEventListener(typeEvent, fn, isDelegated); - } - function removeHandler(element, events, typeEvent, handler, delegationSelector) { - const fn = findHandler(events[typeEvent], handler, delegationSelector); - if (!fn) { - return; - } - element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); - delete events[typeEvent][fn.uidEvent]; - } - function removeNamespacedHandlers(element, events, typeEvent, namespace) { - const storeElementEvent = events[typeEvent] || {}; - for (const [handlerKey, event] of Object.entries(storeElementEvent)) { - if (handlerKey.includes(namespace)) { - removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); - } - } - } - function getTypeEvent(event) { - // allow to get the native events from namespaced events ('click.bs.button' --> 'click') - event = event.replace(stripNameRegex, ''); - return customEvents[event] || event; - } - const EventHandler = { - on(element, event, handler, delegationFunction) { - addHandler(element, event, handler, delegationFunction, false); - }, - one(element, event, handler, delegationFunction) { - addHandler(element, event, handler, delegationFunction, true); - }, - off(element, originalTypeEvent, handler, delegationFunction) { - if (typeof originalTypeEvent !== 'string' || !element) { - return; - } - const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); - const inNamespace = typeEvent !== originalTypeEvent; - const events = getElementEvents(element); - const storeElementEvent = events[typeEvent] || {}; - const isNamespace = originalTypeEvent.startsWith('.'); - if (typeof callable !== 'undefined') { - // Simplest case: handler is passed, remove that listener ONLY. - if (!Object.keys(storeElementEvent).length) { - return; - } - removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); - return; - } - if (isNamespace) { - for (const elementEvent of Object.keys(events)) { - removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); - } - } - for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { - const handlerKey = keyHandlers.replace(stripUidRegex, ''); - if (!inNamespace || originalTypeEvent.includes(handlerKey)) { - removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); - } - } - }, - trigger(element, event, args) { - if (typeof event !== 'string' || !element) { - return null; - } - const $ = getjQuery(); - const typeEvent = getTypeEvent(event); - const inNamespace = event !== typeEvent; - let jQueryEvent = null; - let bubbles = true; - let nativeDispatch = true; - let defaultPrevented = false; - if (inNamespace && $) { - jQueryEvent = $.Event(event, args); - $(element).trigger(jQueryEvent); - bubbles = !jQueryEvent.isPropagationStopped(); - nativeDispatch = !jQueryEvent.isImmediatePropagationStopped(); - defaultPrevented = jQueryEvent.isDefaultPrevented(); - } - const evt = hydrateObj(new Event(event, { - bubbles, - cancelable: true - }), args); - if (defaultPrevented) { - evt.preventDefault(); - } - if (nativeDispatch) { - element.dispatchEvent(evt); - } - if (evt.defaultPrevented && jQueryEvent) { - jQueryEvent.preventDefault(); - } - return evt; - } - }; - function hydrateObj(obj, meta = {}) { - for (const [key, value] of Object.entries(meta)) { - try { - obj[key] = value; - } catch (_unused) { - Object.defineProperty(obj, key, { - configurable: true, - get() { - return value; - } + + function areValidElements() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return !args.some(function (element) { + return !(element && typeof element.getBoundingClientRect === 'function'); }); - } - } - return obj; - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/manipulator.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - function normalizeData(value) { - if (value === 'true') { - return true; - } - if (value === 'false') { - return false; - } - if (value === Number(value).toString()) { - return Number(value); - } - if (value === '' || value === 'null') { - return null; } - if (typeof value !== 'string') { - return value; - } - try { - return JSON.parse(decodeURIComponent(value)); - } catch (_unused) { - return value; - } - } - function normalizeDataKey(key) { - return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); - } - const Manipulator = { - setDataAttribute(element, key, value) { - element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); - }, - removeDataAttribute(element, key) { - element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); - }, - getDataAttributes(element) { - if (!element) { - return {}; - } - const attributes = {}; - const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig')); - for (const key of bsKeys) { - let pureKey = key.replace(/^bs/, ''); - pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length); - attributes[pureKey] = normalizeData(element.dataset[key]); - } - return attributes; - }, - getDataAttribute(element, key) { - return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); - } - }; - /** - * -------------------------------------------------------------------------- - * Bootstrap util/config.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ + function popperGenerator(generatorOptions) { + if (generatorOptions === void 0) { + generatorOptions = {}; + } + var _generatorOptions = generatorOptions, + _generatorOptions$def = _generatorOptions.defaultModifiers, + defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def, + _generatorOptions$def2 = _generatorOptions.defaultOptions, + defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2; + return function createPopper(reference, popper, options) { + if (options === void 0) { + options = defaultOptions; + } - /** - * Class definition - */ + var state = { + placement: 'bottom', + orderedModifiers: [], + options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions), + modifiersData: {}, + elements: { + reference: reference, + popper: popper + }, + attributes: {}, + styles: {} + }; + var effectCleanupFns = []; + var isDestroyed = false; + var instance = { + state: state, + setOptions: function setOptions(setOptionsAction) { + var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction; + cleanupModifierEffects(); + state.options = Object.assign({}, defaultOptions, state.options, options); + state.scrollParents = { + reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [], + popper: listScrollParents(popper) + }; // Orders the modifiers based on their dependencies and `phase` + // properties + + var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers + + state.orderedModifiers = orderedModifiers.filter(function (m) { + return m.enabled; + }); + runModifierEffects(); + return instance.update(); + }, + // Sync update – it will always be executed, even if not necessary. This + // is useful for low frequency updates where sync behavior simplifies the + // logic. + // For high frequency updates (e.g. `resize` and `scroll` events), always + // prefer the async Popper#update method + forceUpdate: function forceUpdate() { + if (isDestroyed) { + return; + } + + var _state$elements = state.elements, + reference = _state$elements.reference, + popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements + // anymore + + if (!areValidElements(reference, popper)) { + return; + } // Store the reference and popper rects to be read by modifiers + + + state.rects = { + reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'), + popper: getLayoutRect(popper) + }; // Modifiers have the ability to reset the current update cycle. The + // most common use case for this is the `flip` modifier changing the + // placement, which then needs to re-run all the modifiers, because the + // logic was previously ran for the previous placement and is therefore + // stale/incorrect + + state.reset = false; + state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier + // is filled with the initial data specified by the modifier. This means + // it doesn't persist and is fresh on each update. + // To ensure persistent data, use `${name}#persistent` + + state.orderedModifiers.forEach(function (modifier) { + return state.modifiersData[modifier.name] = Object.assign({}, modifier.data); + }); + + for (var index = 0; index < state.orderedModifiers.length; index++) { + if (state.reset === true) { + state.reset = false; + index = -1; + continue; + } + + var _state$orderedModifie = state.orderedModifiers[index], + fn = _state$orderedModifie.fn, + _state$orderedModifie2 = _state$orderedModifie.options, + _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2, + name = _state$orderedModifie.name; + + if (typeof fn === 'function') { + state = fn({ + state: state, + options: _options, + name: name, + instance: instance + }) || state; + } + } + }, + // Async and optimistically optimized update – it will not be executed if + // not necessary (debounced to run at most once-per-tick) + update: debounce(function () { + return new Promise(function (resolve) { + instance.forceUpdate(); + resolve(state); + }); + }), + destroy: function destroy() { + cleanupModifierEffects(); + isDestroyed = true; + } + }; + + if (!areValidElements(reference, popper)) { + return instance; + } - class Config { - // Getters - static get Default() { - return {}; - } - static get DefaultType() { - return {}; - } - static get NAME() { - throw new Error('You have to implement the static method "NAME", for each component!'); - } - _getConfig(config) { - config = this._mergeConfigObj(config); - config = this._configAfterMerge(config); - this._typeCheckConfig(config); - return config; - } - _configAfterMerge(config) { - return config; - } - _mergeConfigObj(config, element) { - const jsonConfig = isElement$1(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse - - return { - ...this.constructor.Default, - ...(typeof jsonConfig === 'object' ? jsonConfig : {}), - ...(isElement$1(element) ? Manipulator.getDataAttributes(element) : {}), - ...(typeof config === 'object' ? config : {}) - }; - } - _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { - for (const [property, expectedTypes] of Object.entries(configTypes)) { - const value = config[property]; - const valueType = isElement$1(value) ? 'element' : toType(value); - if (!new RegExp(expectedTypes).test(valueType)) { - throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); - } - } - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap base-component.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const VERSION = '5.3.3'; - - /** - * Class definition - */ - - class BaseComponent extends Config { - constructor(element, config) { - super(); - element = getElement(element); - if (!element) { - return; - } - this._element = element; - this._config = this._getConfig(config); - Data.set(this._element, this.constructor.DATA_KEY, this); - } + instance.setOptions(options).then(function (state) { + if (!isDestroyed && options.onFirstUpdate) { + options.onFirstUpdate(state); + } + }); // Modifiers have the ability to execute arbitrary code before the first + // update cycle runs. They will be executed in the same order as the update + // cycle. This is useful when a modifier adds some persistent data that + // other modifiers need to use, but the modifier is run after the dependent + // one. + + function runModifierEffects() { + state.orderedModifiers.forEach(function (_ref) { + var name = _ref.name, + _ref$options = _ref.options, + options = _ref$options === void 0 ? {} : _ref$options, + effect = _ref.effect; + + if (typeof effect === 'function') { + var cleanupFn = effect({ + state: state, + name: name, + instance: instance, + options: options + }); + + var noopFn = function noopFn() { + }; + + effectCleanupFns.push(cleanupFn || noopFn); + } + }); + } - // Public - dispose() { - Data.remove(this._element, this.constructor.DATA_KEY); - EventHandler.off(this._element, this.constructor.EVENT_KEY); - for (const propertyName of Object.getOwnPropertyNames(this)) { - this[propertyName] = null; - } - } - _queueCallback(callback, element, isAnimated = true) { - executeAfterTransition(callback, element, isAnimated); - } - _getConfig(config) { - config = this._mergeConfigObj(config, this._element); - config = this._configAfterMerge(config); - this._typeCheckConfig(config); - return config; - } + function cleanupModifierEffects() { + effectCleanupFns.forEach(function (fn) { + return fn(); + }); + effectCleanupFns = []; + } - // Static - static getInstance(element) { - return Data.get(getElement(element), this.DATA_KEY); - } - static getOrCreateInstance(element, config = {}) { - return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); - } - static get VERSION() { - return VERSION; - } - static get DATA_KEY() { - return `bs.${this.NAME}`; - } - static get EVENT_KEY() { - return `.${this.DATA_KEY}`; - } - static eventName(name) { - return `${name}${this.EVENT_KEY}`; + return instance; + }; } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/selector-engine.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - const getSelector = element => { - let selector = element.getAttribute('data-bs-target'); - if (!selector || selector === '#') { - let hrefAttribute = element.getAttribute('href'); - - // The only valid content that could double as a selector are IDs or classes, - // so everything starting with `#` or `.`. If a "real" URL is used as the selector, - // `document.querySelector` will rightfully complain it is invalid. - // See https://github.com/twbs/bootstrap/issues/32273 - if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { - return null; - } - // Just in case some CMS puts out a full URL with the anchor appended - if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { - hrefAttribute = `#${hrefAttribute.split('#')[1]}`; - } - selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; - } - return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null; - }; - const SelectorEngine = { - find(selector, element = document.documentElement) { - return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); - }, - findOne(selector, element = document.documentElement) { - return Element.prototype.querySelector.call(element, selector); - }, - children(element, selector) { - return [].concat(...element.children).filter(child => child.matches(selector)); - }, - parents(element, selector) { - const parents = []; - let ancestor = element.parentNode.closest(selector); - while (ancestor) { - parents.push(ancestor); - ancestor = ancestor.parentNode.closest(selector); - } - return parents; - }, - prev(element, selector) { - let previous = element.previousElementSibling; - while (previous) { - if (previous.matches(selector)) { - return [previous]; - } - previous = previous.previousElementSibling; - } - return []; - }, - // TODO: this is now unused; remove later along with prev() - next(element, selector) { - let next = element.nextElementSibling; - while (next) { - if (next.matches(selector)) { - return [next]; - } - next = next.nextElementSibling; - } - return []; - }, - focusableChildren(element) { - const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); - return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el)); - }, - getSelectorFromElement(element) { - const selector = getSelector(element); - if (selector) { - return SelectorEngine.findOne(selector) ? selector : null; - } - return null; - }, - getElementFromSelector(element) { - const selector = getSelector(element); - return selector ? SelectorEngine.findOne(selector) : null; - }, - getMultipleElementsFromSelector(element) { - const selector = getSelector(element); - return selector ? SelectorEngine.find(selector) : []; - } - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/component-functions.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - const enableDismissTrigger = (component, method = 'hide') => { - const clickEvent = `click.dismiss${component.EVENT_KEY}`; - const name = component.NAME; - EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { - if (['A', 'AREA'].includes(this.tagName)) { - event.preventDefault(); - } - if (isDisabled(this)) { - return; - } - const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`); - const instance = component.getOrCreateInstance(target); - - // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method - instance[method](); - }); - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap alert.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$f = 'alert'; - const DATA_KEY$a = 'bs.alert'; - const EVENT_KEY$b = `.${DATA_KEY$a}`; - const EVENT_CLOSE = `close${EVENT_KEY$b}`; - const EVENT_CLOSED = `closed${EVENT_KEY$b}`; - const CLASS_NAME_FADE$5 = 'fade'; - const CLASS_NAME_SHOW$8 = 'show'; - - /** - * Class definition - */ - - class Alert extends BaseComponent { - // Getters - static get NAME() { - return NAME$f; - } + var createPopper$2 = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules + + var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1]; + var createPopper$1 = /*#__PURE__*/popperGenerator({ + defaultModifiers: defaultModifiers$1 + }); // eslint-disable-next-line import/no-unused-modules + + var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1]; + var createPopper = /*#__PURE__*/popperGenerator({ + defaultModifiers: defaultModifiers + }); // eslint-disable-next-line import/no-unused-modules + + const Popper = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ + __proto__: null, + afterMain, + afterRead, + afterWrite, + applyStyles: applyStyles$1, + arrow: arrow$1, + auto, + basePlacements, + beforeMain, + beforeRead, + beforeWrite, + bottom, + clippingParents, + computeStyles: computeStyles$1, + createPopper, + createPopperBase: createPopper$2, + createPopperLite: createPopper$1, + detectOverflow, + end, + eventListeners, + flip: flip$1, + hide: hide$1, + left, + main, + modifierPhases, + offset: offset$1, + placements, + popper, + popperGenerator, + popperOffsets: popperOffsets$1, + preventOverflow: preventOverflow$1, + read, + reference, + right, + start, + top, + variationPlacements, + viewport, + write + }, Symbol.toStringTag, {value: 'Module'})); - // Public - close() { - const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE); - if (closeEvent.defaultPrevented) { - return; - } - this._element.classList.remove(CLASS_NAME_SHOW$8); - const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5); - this._queueCallback(() => this._destroyElement(), this._element, isAnimated); - } + /** + * -------------------------------------------------------------------------- + * Bootstrap dropdown.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - // Private - _destroyElement() { - this._element.remove(); - EventHandler.trigger(this._element, EVENT_CLOSED); - this.dispose(); - } - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Alert.getOrCreateInstance(this); - if (typeof config !== 'string') { - return; + /** + * Constants + */ + + const NAME$a = 'dropdown'; + const DATA_KEY$6 = 'bs.dropdown'; + const EVENT_KEY$6 = `.${DATA_KEY$6}`; + const DATA_API_KEY$3 = '.data-api'; + const ESCAPE_KEY$2 = 'Escape'; + const TAB_KEY$1 = 'Tab'; + const ARROW_UP_KEY$1 = 'ArrowUp'; + const ARROW_DOWN_KEY$1 = 'ArrowDown'; + const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button + + const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`; + const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`; + const EVENT_SHOW$5 = `show${EVENT_KEY$6}`; + const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`; + const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`; + const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`; + const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`; + const CLASS_NAME_SHOW$6 = 'show'; + const CLASS_NAME_DROPUP = 'dropup'; + const CLASS_NAME_DROPEND = 'dropend'; + const CLASS_NAME_DROPSTART = 'dropstart'; + const CLASS_NAME_DROPUP_CENTER = 'dropup-center'; + const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'; + const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'; + const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`; + const SELECTOR_MENU = '.dropdown-menu'; + const SELECTOR_NAVBAR = '.navbar'; + const SELECTOR_NAVBAR_NAV = '.navbar-nav'; + const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; + const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'; + const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'; + const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'; + const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'; + const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'; + const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'; + const PLACEMENT_TOPCENTER = 'top'; + const PLACEMENT_BOTTOMCENTER = 'bottom'; + const Default$9 = { + autoClose: true, + boundary: 'clippingParents', + display: 'dynamic', + offset: [0, 2], + popperConfig: null, + reference: 'toggle' + }; + const DefaultType$9 = { + autoClose: '(boolean|string)', + boundary: '(string|element)', + display: 'string', + offset: '(array|string|function)', + popperConfig: '(null|object|function)', + reference: '(string|element|object)' + }; + + /** + * Class definition + */ + + class Dropdown extends BaseComponent { + constructor(element, config) { + super(element, config); + this._popper = null; + this._parent = this._element.parentNode; // dropdown wrapper + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent); + this._inNavbar = this._detectNavbar(); } - if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { - throw new TypeError(`No method named "${config}"`); + + // Getters + static get Default() { + return Default$9; + } + + static get DefaultType() { + return DefaultType$9; + } + + static get NAME() { + return NAME$a; } - data[config](this); - }); - } - } - /** - * Data API implementation - */ + // Public + toggle() { + return this._isShown() ? this.hide() : this.show(); + } - enableDismissTrigger(Alert, 'close'); + show() { + if (isDisabled(this._element) || this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget); + if (showEvent.defaultPrevented) { + return; + } + this._createPopper(); + + // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) { + for (const element of [].concat(...document.body.children)) { + EventHandler.on(element, 'mouseover', noop); + } + } + this._element.focus(); + this._element.setAttribute('aria-expanded', true); + this._menu.classList.add(CLASS_NAME_SHOW$6); + this._element.classList.add(CLASS_NAME_SHOW$6); + EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget); + } - /** - * jQuery - */ + hide() { + if (isDisabled(this._element) || !this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + this._completeHide(relatedTarget); + } - defineJQueryPlugin(Alert); + dispose() { + if (this._popper) { + this._popper.destroy(); + } + super.dispose(); + } - /** - * -------------------------------------------------------------------------- - * Bootstrap button.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ + update() { + this._inNavbar = this._detectNavbar(); + if (this._popper) { + this._popper.update(); + } + } + // Private + _completeHide(relatedTarget) { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget); + if (hideEvent.defaultPrevented) { + return; + } - /** - * Constants - */ + // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.off(element, 'mouseover', noop); + } + } + if (this._popper) { + this._popper.destroy(); + } + this._menu.classList.remove(CLASS_NAME_SHOW$6); + this._element.classList.remove(CLASS_NAME_SHOW$6); + this._element.setAttribute('aria-expanded', 'false'); + Manipulator.removeDataAttribute(this._menu, 'popper'); + EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget); + } - const NAME$e = 'button'; - const DATA_KEY$9 = 'bs.button'; - const EVENT_KEY$a = `.${DATA_KEY$9}`; - const DATA_API_KEY$6 = '.data-api'; - const CLASS_NAME_ACTIVE$3 = 'active'; - const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle="button"]'; - const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`; + _getConfig(config) { + config = super._getConfig(config); + if (typeof config.reference === 'object' && !isElement$1(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { + // Popper virtual elements require a getBoundingClientRect method + throw new TypeError(`${NAME$a.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); + } + return config; + } - /** - * Class definition - */ + _createPopper() { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)'); + } + let referenceElement = this._element; + if (this._config.reference === 'parent') { + referenceElement = this._parent; + } else if (isElement$1(this._config.reference)) { + referenceElement = getElement(this._config.reference); + } else if (typeof this._config.reference === 'object') { + referenceElement = this._config.reference; + } + const popperConfig = this._getPopperConfig(); + this._popper = createPopper(referenceElement, this._menu, popperConfig); + } - class Button extends BaseComponent { - // Getters - static get NAME() { - return NAME$e; - } + _isShown() { + return this._menu.classList.contains(CLASS_NAME_SHOW$6); + } - // Public - toggle() { - // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method - this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3)); - } + _getPlacement() { + const parentDropdown = this._parent; + if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { + return PLACEMENT_RIGHT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { + return PLACEMENT_LEFT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) { + return PLACEMENT_TOPCENTER; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) { + return PLACEMENT_BOTTOMCENTER; + } - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Button.getOrCreateInstance(this); - if (config === 'toggle') { - data[config](); + // We need to trim the value because custom properties can also include spaces + const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'; + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { + return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP; + } + return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM; } - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => { - event.preventDefault(); - const button = event.target.closest(SELECTOR_DATA_TOGGLE$5); - const data = Button.getOrCreateInstance(button); - data.toggle(); - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Button); - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/swipe.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$d = 'swipe'; - const EVENT_KEY$9 = '.bs.swipe'; - const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`; - const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`; - const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`; - const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`; - const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`; - const POINTER_TYPE_TOUCH = 'touch'; - const POINTER_TYPE_PEN = 'pen'; - const CLASS_NAME_POINTER_EVENT = 'pointer-event'; - const SWIPE_THRESHOLD = 40; - const Default$c = { - endCallback: null, - leftCallback: null, - rightCallback: null - }; - const DefaultType$c = { - endCallback: '(function|null)', - leftCallback: '(function|null)', - rightCallback: '(function|null)' - }; - - /** - * Class definition - */ - - class Swipe extends Config { - constructor(element, config) { - super(); - this._element = element; - if (!element || !Swipe.isSupported()) { - return; - } - this._config = this._getConfig(config); - this._deltaX = 0; - this._supportPointerEvents = Boolean(window.PointerEvent); - this._initEvents(); - } - // Getters - static get Default() { - return Default$c; - } - static get DefaultType() { - return DefaultType$c; - } - static get NAME() { - return NAME$d; - } + _detectNavbar() { + return this._element.closest(SELECTOR_NAVBAR) !== null; + } - // Public - dispose() { - EventHandler.off(this._element, EVENT_KEY$9); - } + _getOffset() { + const { + offset + } = this._config; + if (typeof offset === 'string') { + return offset.split(',').map(value => Number.parseInt(value, 10)); + } + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element); + } + return offset; + } - // Private - _start(event) { - if (!this._supportPointerEvents) { - this._deltaX = event.touches[0].clientX; - return; - } - if (this._eventIsPointerPenTouch(event)) { - this._deltaX = event.clientX; - } - } - _end(event) { - if (this._eventIsPointerPenTouch(event)) { - this._deltaX = event.clientX - this._deltaX; - } - this._handleSwipe(); - execute(this._config.endCallback); - } - _move(event) { - this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX; - } - _handleSwipe() { - const absDeltaX = Math.abs(this._deltaX); - if (absDeltaX <= SWIPE_THRESHOLD) { - return; - } - const direction = absDeltaX / this._deltaX; - this._deltaX = 0; - if (!direction) { - return; - } - execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback); - } - _initEvents() { - if (this._supportPointerEvents) { - EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event)); - EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event)); - this._element.classList.add(CLASS_NAME_POINTER_EVENT); - } else { - EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event)); - EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event)); - EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event)); - } - } - _eventIsPointerPenTouch(event) { - return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH); - } + _getPopperConfig() { + const defaultBsPopperConfig = { + placement: this._getPlacement(), + modifiers: [{ + name: 'preventOverflow', + options: { + boundary: this._config.boundary + } + }, { + name: 'offset', + options: { + offset: this._getOffset() + } + }] + }; + + // Disable Popper if we have a static display or Dropdown is in Navbar + if (this._inNavbar || this._config.display === 'static') { + Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove + defaultBsPopperConfig.modifiers = [{ + name: 'applyStyles', + enabled: false + }]; + } + return { + ...defaultBsPopperConfig, + ...execute(this._config.popperConfig, [defaultBsPopperConfig]) + }; + } - // Static - static isSupported() { - return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap carousel.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$c = 'carousel'; - const DATA_KEY$8 = 'bs.carousel'; - const EVENT_KEY$8 = `.${DATA_KEY$8}`; - const DATA_API_KEY$5 = '.data-api'; - const ARROW_LEFT_KEY$1 = 'ArrowLeft'; - const ARROW_RIGHT_KEY$1 = 'ArrowRight'; - const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch - - const ORDER_NEXT = 'next'; - const ORDER_PREV = 'prev'; - const DIRECTION_LEFT = 'left'; - const DIRECTION_RIGHT = 'right'; - const EVENT_SLIDE = `slide${EVENT_KEY$8}`; - const EVENT_SLID = `slid${EVENT_KEY$8}`; - const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`; - const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`; - const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`; - const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`; - const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`; - const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`; - const CLASS_NAME_CAROUSEL = 'carousel'; - const CLASS_NAME_ACTIVE$2 = 'active'; - const CLASS_NAME_SLIDE = 'slide'; - const CLASS_NAME_END = 'carousel-item-end'; - const CLASS_NAME_START = 'carousel-item-start'; - const CLASS_NAME_NEXT = 'carousel-item-next'; - const CLASS_NAME_PREV = 'carousel-item-prev'; - const SELECTOR_ACTIVE = '.active'; - const SELECTOR_ITEM = '.carousel-item'; - const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM; - const SELECTOR_ITEM_IMG = '.carousel-item img'; - const SELECTOR_INDICATORS = '.carousel-indicators'; - const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'; - const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'; - const KEY_TO_DIRECTION = { - [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT, - [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT - }; - const Default$b = { - interval: 5000, - keyboard: true, - pause: 'hover', - ride: false, - touch: true, - wrap: true - }; - const DefaultType$b = { - interval: '(number|boolean)', - // TODO:v6 remove boolean support - keyboard: 'boolean', - pause: '(string|boolean)', - ride: '(boolean|string)', - touch: 'boolean', - wrap: 'boolean' - }; - - /** - * Class definition - */ - - class Carousel extends BaseComponent { - constructor(element, config) { - super(element, config); - this._interval = null; - this._activeElement = null; - this._isSliding = false; - this.touchTimeout = null; - this._swipeHelper = null; - this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element); - this._addEventListeners(); - if (this._config.ride === CLASS_NAME_CAROUSEL) { - this.cycle(); - } - } + _selectMenuItem({ + key, + target + }) { + const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element)); + if (!items.length) { + return; + } - // Getters - static get Default() { - return Default$b; - } - static get DefaultType() { - return DefaultType$b; - } - static get NAME() { - return NAME$c; - } + // if target isn't included in items (e.g. when expanding the dropdown) + // allow cycling to get the last item in case key equals ARROW_UP_KEY + getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus(); + } - // Public - next() { - this._slide(ORDER_NEXT); - } - nextWhenVisible() { - // FIXME TODO use `document.visibilityState` - // Don't call next when the page isn't visible - // or the carousel or its parent isn't visible - if (!document.hidden && isVisible(this._element)) { - this.next(); - } - } - prev() { - this._slide(ORDER_PREV); - } - pause() { - if (this._isSliding) { - triggerTransitionEnd(this._element); - } - this._clearInterval(); - } - cycle() { - this._clearInterval(); - this._updateInterval(); - this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval); - } - _maybeEnableCycle() { - if (!this._config.ride) { - return; - } - if (this._isSliding) { - EventHandler.one(this._element, EVENT_SLID, () => this.cycle()); - return; - } - this.cycle(); - } - to(index) { - const items = this._getItems(); - if (index > items.length - 1 || index < 0) { - return; - } - if (this._isSliding) { - EventHandler.one(this._element, EVENT_SLID, () => this.to(index)); - return; - } - const activeIndex = this._getItemIndex(this._getActive()); - if (activeIndex === index) { - return; - } - const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV; - this._slide(order, items[index]); - } - dispose() { - if (this._swipeHelper) { - this._swipeHelper.dispose(); - } - super.dispose(); - } + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Dropdown.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } - // Private - _configAfterMerge(config) { - config.defaultInterval = config.interval; - return config; - } - _addEventListeners() { - if (this._config.keyboard) { - EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event)); - } - if (this._config.pause === 'hover') { - EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause()); - EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle()); - } - if (this._config.touch && Swipe.isSupported()) { - this._addTouchEventListeners(); - } - } - _addTouchEventListeners() { - for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { - EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault()); - } - const endCallBack = () => { - if (this._config.pause !== 'hover') { - return; - } - - // If it's a touch-enabled device, mouseenter/leave are fired as - // part of the mouse compatibility events on first tap - the carousel - // would stop cycling until user tapped out of it; - // here, we listen for touchend, explicitly pause the carousel - // (as if it's the second time we tap on it, mouseenter compat event - // is NOT fired) and after a timeout (to allow for mouse compatibility - // events to fire) we explicitly restart cycling - - this.pause(); - if (this.touchTimeout) { - clearTimeout(this.touchTimeout); - } - this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval); - }; - const swipeConfig = { - leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)), - rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)), - endCallback: endCallBack - }; - this._swipeHelper = new Swipe(this._element, swipeConfig); + static clearMenus(event) { + if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) { + return; + } + const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN); + for (const toggle of openToggles) { + const context = Dropdown.getInstance(toggle); + if (!context || context._config.autoClose === false) { + continue; + } + const composedPath = event.composedPath(); + const isMenuTarget = composedPath.includes(context._menu); + if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { + continue; + } + + // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu + if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) { + continue; + } + const relatedTarget = { + relatedTarget: context._element + }; + if (event.type === 'click') { + relatedTarget.clickEvent = event; + } + context._completeHide(relatedTarget); + } + } + + static dataApiKeydownHandler(event) { + // If not an UP | DOWN | ESCAPE key => not a dropdown command + // If input/textarea && if key is other than ESCAPE => not a dropdown command + + const isInput = /input|textarea/i.test(event.target.tagName); + const isEscapeEvent = event.key === ESCAPE_KEY$2; + const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key); + if (!isUpOrDownEvent && !isEscapeEvent) { + return; + } + if (isInput && !isEscapeEvent) { + return; + } + event.preventDefault(); + + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode); + const instance = Dropdown.getOrCreateInstance(getToggleButton); + if (isUpOrDownEvent) { + event.stopPropagation(); + instance.show(); + instance._selectMenuItem(event); + return; + } + if (instance._isShown()) { + // else is escape and we check if it is shown + event.stopPropagation(); + instance.hide(); + getToggleButton.focus(); + } + } } - _keydown(event) { - if (/input|textarea/i.test(event.target.tagName)) { - return; - } - const direction = KEY_TO_DIRECTION[event.key]; - if (direction) { + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus); + EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus); + EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) { event.preventDefault(); - this._slide(this._directionToOrder(direction)); - } - } - _getItemIndex(element) { - return this._getItems().indexOf(element); - } - _setActiveIndicatorElement(index) { - if (!this._indicatorsElement) { - return; - } - const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement); - activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2); - activeIndicator.removeAttribute('aria-current'); - const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement); - if (newActiveIndicator) { - newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2); - newActiveIndicator.setAttribute('aria-current', 'true'); - } - } - _updateInterval() { - const element = this._activeElement || this._getActive(); - if (!element) { - return; - } - const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10); - this._config.interval = elementInterval || this._config.defaultInterval; - } - _slide(order, element = null) { - if (this._isSliding) { - return; - } - const activeElement = this._getActive(); - const isNext = order === ORDER_NEXT; - const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap); - if (nextElement === activeElement) { - return; - } - const nextElementIndex = this._getItemIndex(nextElement); - const triggerEvent = eventName => { - return EventHandler.trigger(this._element, eventName, { - relatedTarget: nextElement, - direction: this._orderToDirection(order), - from: this._getItemIndex(activeElement), - to: nextElementIndex - }); - }; - const slideEvent = triggerEvent(EVENT_SLIDE); - if (slideEvent.defaultPrevented) { - return; - } - if (!activeElement || !nextElement) { - // Some weirdness is happening, so we bail - // TODO: change tests that use empty divs to avoid this check - return; - } - const isCycling = Boolean(this._interval); - this.pause(); - this._isSliding = true; - this._setActiveIndicatorElement(nextElementIndex); - this._activeElement = nextElement; - const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END; - const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV; - nextElement.classList.add(orderClassName); - reflow(nextElement); - activeElement.classList.add(directionalClassName); - nextElement.classList.add(directionalClassName); - const completeCallBack = () => { - nextElement.classList.remove(directionalClassName, orderClassName); - nextElement.classList.add(CLASS_NAME_ACTIVE$2); - activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName); - this._isSliding = false; - triggerEvent(EVENT_SLID); - }; - this._queueCallback(completeCallBack, activeElement, this._isAnimated()); - if (isCycling) { - this.cycle(); - } - } - _isAnimated() { - return this._element.classList.contains(CLASS_NAME_SLIDE); - } - _getActive() { - return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element); - } - _getItems() { - return SelectorEngine.find(SELECTOR_ITEM, this._element); - } - _clearInterval() { - if (this._interval) { - clearInterval(this._interval); - this._interval = null; - } - } - _directionToOrder(direction) { - if (isRTL()) { - return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT; - } - return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV; - } - _orderToDirection(order) { - if (isRTL()) { - return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT; - } + Dropdown.getOrCreateInstance(this).toggle(); + }); - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Carousel.getOrCreateInstance(this, config); - if (typeof config === 'number') { - data.to(config); - return; - } - if (typeof config === 'string') { - if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - } - }); - } - } + /** + * jQuery + */ - /** - * Data API implementation - */ + defineJQueryPlugin(Dropdown); - EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) { - const target = SelectorEngine.getElementFromSelector(this); - if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { - return; - } - event.preventDefault(); - const carousel = Carousel.getOrCreateInstance(target); - const slideIndex = this.getAttribute('data-bs-slide-to'); - if (slideIndex) { - carousel.to(slideIndex); - carousel._maybeEnableCycle(); - return; - } - if (Manipulator.getDataAttribute(this, 'slide') === 'next') { - carousel.next(); - carousel._maybeEnableCycle(); - return; - } - carousel.prev(); - carousel._maybeEnableCycle(); - }); - EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => { - const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE); - for (const carousel of carousels) { - Carousel.getOrCreateInstance(carousel); - } - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Carousel); - - /** - * -------------------------------------------------------------------------- - * Bootstrap collapse.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$b = 'collapse'; - const DATA_KEY$7 = 'bs.collapse'; - const EVENT_KEY$7 = `.${DATA_KEY$7}`; - const DATA_API_KEY$4 = '.data-api'; - const EVENT_SHOW$6 = `show${EVENT_KEY$7}`; - const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`; - const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`; - const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`; - const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`; - const CLASS_NAME_SHOW$7 = 'show'; - const CLASS_NAME_COLLAPSE = 'collapse'; - const CLASS_NAME_COLLAPSING = 'collapsing'; - const CLASS_NAME_COLLAPSED = 'collapsed'; - const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`; - const CLASS_NAME_HORIZONTAL = 'collapse-horizontal'; - const WIDTH = 'width'; - const HEIGHT = 'height'; - const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'; - const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle="collapse"]'; - const Default$a = { - parent: null, - toggle: true - }; - const DefaultType$a = { - parent: '(null|element)', - toggle: 'boolean' - }; - - /** - * Class definition - */ - - class Collapse extends BaseComponent { - constructor(element, config) { - super(element, config); - this._isTransitioning = false; - this._triggerArray = []; - const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4); - for (const elem of toggleList) { - const selector = SelectorEngine.getSelectorFromElement(elem); - const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element); - if (selector !== null && filterElement.length) { - this._triggerArray.push(elem); - } - } - this._initializeChildren(); - if (!this._config.parent) { - this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()); - } - if (this._config.toggle) { - this.toggle(); - } - } + /** + * -------------------------------------------------------------------------- + * Bootstrap util/backdrop.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - // Getters - static get Default() { - return Default$a; - } - static get DefaultType() { - return DefaultType$a; - } - static get NAME() { - return NAME$b; - } - // Public - toggle() { - if (this._isShown()) { - this.hide(); - } else { - this.show(); - } - } - show() { - if (this._isTransitioning || this._isShown()) { - return; - } - let activeChildren = []; - - // find active children - if (this._config.parent) { - activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, { - toggle: false - })); - } - if (activeChildren.length && activeChildren[0]._isTransitioning) { - return; - } - const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6); - if (startEvent.defaultPrevented) { - return; - } - for (const activeInstance of activeChildren) { - activeInstance.hide(); - } - const dimension = this._getDimension(); - this._element.classList.remove(CLASS_NAME_COLLAPSE); - this._element.classList.add(CLASS_NAME_COLLAPSING); - this._element.style[dimension] = 0; - this._addAriaAndCollapsedClass(this._triggerArray, true); - this._isTransitioning = true; - const complete = () => { - this._isTransitioning = false; - this._element.classList.remove(CLASS_NAME_COLLAPSING); - this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); - this._element.style[dimension] = ''; - EventHandler.trigger(this._element, EVENT_SHOWN$6); - }; - const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); - const scrollSize = `scroll${capitalizedDimension}`; - this._queueCallback(complete, this._element, true); - this._element.style[dimension] = `${this._element[scrollSize]}px`; - } - hide() { - if (this._isTransitioning || !this._isShown()) { - return; - } - const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6); - if (startEvent.defaultPrevented) { - return; - } - const dimension = this._getDimension(); - this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`; - reflow(this._element); - this._element.classList.add(CLASS_NAME_COLLAPSING); - this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); - for (const trigger of this._triggerArray) { - const element = SelectorEngine.getElementFromSelector(trigger); - if (element && !this._isShown(element)) { - this._addAriaAndCollapsedClass([trigger], false); - } - } - this._isTransitioning = true; - const complete = () => { - this._isTransitioning = false; - this._element.classList.remove(CLASS_NAME_COLLAPSING); - this._element.classList.add(CLASS_NAME_COLLAPSE); - EventHandler.trigger(this._element, EVENT_HIDDEN$6); - }; - this._element.style[dimension] = ''; - this._queueCallback(complete, this._element, true); - } - _isShown(element = this._element) { - return element.classList.contains(CLASS_NAME_SHOW$7); - } + /** + * Constants + */ - // Private - _configAfterMerge(config) { - config.toggle = Boolean(config.toggle); // Coerce string values - config.parent = getElement(config.parent); - return config; - } - _getDimension() { - return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT; - } - _initializeChildren() { - if (!this._config.parent) { - return; - } - const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4); - for (const element of children) { - const selected = SelectorEngine.getElementFromSelector(element); - if (selected) { - this._addAriaAndCollapsedClass([element], this._isShown(selected)); - } - } - } - _getFirstLevelChildren(selector) { - const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); - // remove children if greater depth - return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element)); - } - _addAriaAndCollapsedClass(triggerArray, isOpen) { - if (!triggerArray.length) { - return; - } - for (const element of triggerArray) { - element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen); - element.setAttribute('aria-expanded', isOpen); - } - } + const NAME$9 = 'backdrop'; + const CLASS_NAME_FADE$4 = 'fade'; + const CLASS_NAME_SHOW$5 = 'show'; + const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`; + const Default$8 = { + className: 'modal-backdrop', + clickCallback: null, + isAnimated: false, + isVisible: true, + // if false, we use the backdrop helper without adding any element to the dom + rootElement: 'body' // give the choice to place backdrop under different elements + }; + const DefaultType$8 = { + className: 'string', + clickCallback: '(function|null)', + isAnimated: 'boolean', + isVisible: 'boolean', + rootElement: '(element|string)' + }; + + /** + * Class definition + */ + + class Backdrop extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + this._isAppended = false; + this._element = null; + } + + // Getters + static get Default() { + return Default$8; + } - // Static - static jQueryInterface(config) { - const _config = {}; - if (typeof config === 'string' && /show|hide/.test(config)) { - _config.toggle = false; - } - return this.each(function () { - const data = Collapse.getOrCreateInstance(this, _config); - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - } - }); - } - } + static get DefaultType() { + return DefaultType$8; + } - /** - * Data API implementation - */ + static get NAME() { + return NAME$9; + } - EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) { - // preventDefault only for elements (which change the URL) not inside the collapsible element - if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') { - event.preventDefault(); - } - for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) { - Collapse.getOrCreateInstance(element, { - toggle: false - }).toggle(); - } - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Collapse); - - var top = 'top'; - var bottom = 'bottom'; - var right = 'right'; - var left = 'left'; - var auto = 'auto'; - var basePlacements = [top, bottom, right, left]; - var start = 'start'; - var end = 'end'; - var clippingParents = 'clippingParents'; - var viewport = 'viewport'; - var popper = 'popper'; - var reference = 'reference'; - var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) { - return acc.concat([placement + "-" + start, placement + "-" + end]); - }, []); - var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) { - return acc.concat([placement, placement + "-" + start, placement + "-" + end]); - }, []); // modifiers that need to read the DOM - - var beforeRead = 'beforeRead'; - var read = 'read'; - var afterRead = 'afterRead'; // pure-logic modifiers - - var beforeMain = 'beforeMain'; - var main = 'main'; - var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state) - - var beforeWrite = 'beforeWrite'; - var write = 'write'; - var afterWrite = 'afterWrite'; - var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite]; - - function getNodeName(element) { - return element ? (element.nodeName || '').toLowerCase() : null; - } - - function getWindow(node) { - if (node == null) { - return window; - } + // Public + show(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + this._append(); + const element = this._getElement(); + if (this._config.isAnimated) { + reflow(element); + } + element.classList.add(CLASS_NAME_SHOW$5); + this._emulateAnimation(() => { + execute(callback); + }); + } - if (node.toString() !== '[object Window]') { - var ownerDocument = node.ownerDocument; - return ownerDocument ? ownerDocument.defaultView || window : window; - } + hide(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + this._getElement().classList.remove(CLASS_NAME_SHOW$5); + this._emulateAnimation(() => { + this.dispose(); + execute(callback); + }); + } + + dispose() { + if (!this._isAppended) { + return; + } + EventHandler.off(this._element, EVENT_MOUSEDOWN); + this._element.remove(); + this._isAppended = false; + } - return node; - } + // Private + _getElement() { + if (!this._element) { + const backdrop = document.createElement('div'); + backdrop.className = this._config.className; + if (this._config.isAnimated) { + backdrop.classList.add(CLASS_NAME_FADE$4); + } + this._element = backdrop; + } + return this._element; + } - function isElement(node) { - var OwnElement = getWindow(node).Element; - return node instanceof OwnElement || node instanceof Element; - } + _configAfterMerge(config) { + // use getElement() with the default "body" to get a fresh Element on each instantiation + config.rootElement = getElement(config.rootElement); + return config; + } - function isHTMLElement(node) { - var OwnElement = getWindow(node).HTMLElement; - return node instanceof OwnElement || node instanceof HTMLElement; - } + _append() { + if (this._isAppended) { + return; + } + const element = this._getElement(); + this._config.rootElement.append(element); + EventHandler.on(element, EVENT_MOUSEDOWN, () => { + execute(this._config.clickCallback); + }); + this._isAppended = true; + } - function isShadowRoot(node) { - // IE 11 has no ShadowRoot - if (typeof ShadowRoot === 'undefined') { - return false; + _emulateAnimation(callback) { + executeAfterTransition(callback, this._getElement(), this._config.isAnimated); + } } - var OwnElement = getWindow(node).ShadowRoot; - return node instanceof OwnElement || node instanceof ShadowRoot; - } - - // and applies them to the HTMLElements such as popper and arrow + /** + * -------------------------------------------------------------------------- + * Bootstrap util/focustrap.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - function applyStyles(_ref) { - var state = _ref.state; - Object.keys(state.elements).forEach(function (name) { - var style = state.styles[name] || {}; - var attributes = state.attributes[name] || {}; - var element = state.elements[name]; // arrow is optional + virtual elements - if (!isHTMLElement(element) || !getNodeName(element)) { - return; - } // Flow doesn't support to extend this property, but it's the most - // effective way to apply styles to an HTMLElement - // $FlowFixMe[cannot-write] + /** + * Constants + */ + const NAME$8 = 'focustrap'; + const DATA_KEY$5 = 'bs.focustrap'; + const EVENT_KEY$5 = `.${DATA_KEY$5}`; + const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`; + const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`; + const TAB_KEY = 'Tab'; + const TAB_NAV_FORWARD = 'forward'; + const TAB_NAV_BACKWARD = 'backward'; + const Default$7 = { + autofocus: true, + trapElement: null // The element to trap focus inside of + }; + const DefaultType$7 = { + autofocus: 'boolean', + trapElement: 'element' + }; - Object.assign(element.style, style); - Object.keys(attributes).forEach(function (name) { - var value = attributes[name]; + /** + * Class definition + */ - if (value === false) { - element.removeAttribute(name); - } else { - element.setAttribute(name, value === true ? '' : value); + class FocusTrap extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + this._isActive = false; + this._lastTabNavDirection = null; } - }); - }); - } - - function effect$2(_ref2) { - var state = _ref2.state; - var initialStyles = { - popper: { - position: state.options.strategy, - left: '0', - top: '0', - margin: '0' - }, - arrow: { - position: 'absolute' - }, - reference: {} - }; - Object.assign(state.elements.popper.style, initialStyles.popper); - state.styles = initialStyles; - if (state.elements.arrow) { - Object.assign(state.elements.arrow.style, initialStyles.arrow); - } + // Getters + static get Default() { + return Default$7; + } - return function () { - Object.keys(state.elements).forEach(function (name) { - var element = state.elements[name]; - var attributes = state.attributes[name] || {}; - var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them + static get DefaultType() { + return DefaultType$7; + } - var style = styleProperties.reduce(function (style, property) { - style[property] = ''; - return style; - }, {}); // arrow is optional + virtual elements + static get NAME() { + return NAME$8; + } - if (!isHTMLElement(element) || !getNodeName(element)) { - return; + // Public + activate() { + if (this._isActive) { + return; + } + if (this._config.autofocus) { + this._config.trapElement.focus(); + } + EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop + EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event)); + EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); + this._isActive = true; } - Object.assign(element.style, style); - Object.keys(attributes).forEach(function (attribute) { - element.removeAttribute(attribute); - }); - }); - }; - } // eslint-disable-next-line import/no-unused-modules + deactivate() { + if (!this._isActive) { + return; + } + this._isActive = false; + EventHandler.off(document, EVENT_KEY$5); + } + // Private + _handleFocusin(event) { + const { + trapElement + } = this._config; + if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { + return; + } + const elements = SelectorEngine.focusableChildren(trapElement); + if (elements.length === 0) { + trapElement.focus(); + } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { + elements[elements.length - 1].focus(); + } else { + elements[0].focus(); + } + } - const applyStyles$1 = { - name: 'applyStyles', - enabled: true, - phase: 'write', - fn: applyStyles, - effect: effect$2, - requires: ['computeStyles'] - }; + _handleKeydown(event) { + if (event.key !== TAB_KEY) { + return; + } + this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; + } + } - function getBasePlacement(placement) { - return placement.split('-')[0]; - } + /** + * -------------------------------------------------------------------------- + * Bootstrap util/scrollBar.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - var max = Math.max; - var min = Math.min; - var round = Math.round; - function getUAString() { - var uaData = navigator.userAgentData; + /** + * Constants + */ - if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) { - return uaData.brands.map(function (item) { - return item.brand + "/" + item.version; - }).join(' '); - } + const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; + const SELECTOR_STICKY_CONTENT = '.sticky-top'; + const PROPERTY_PADDING = 'padding-right'; + const PROPERTY_MARGIN = 'margin-right'; - return navigator.userAgent; - } + /** + * Class definition + */ - function isLayoutViewport() { - return !/^((?!chrome|android).)*safari/i.test(getUAString()); - } + class ScrollBarHelper { + constructor() { + this._element = document.body; + } - function getBoundingClientRect(element, includeScale, isFixedStrategy) { - if (includeScale === void 0) { - includeScale = false; - } + // Public + getWidth() { + // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes + const documentWidth = document.documentElement.clientWidth; + return Math.abs(window.innerWidth - documentWidth); + } - if (isFixedStrategy === void 0) { - isFixedStrategy = false; - } + hide() { + const width = this.getWidth(); + this._disableOverFlow(); + // give padding to element to balance the hidden scrollbar width + this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); + // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth + this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width); + this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width); + } - var clientRect = element.getBoundingClientRect(); - var scaleX = 1; - var scaleY = 1; + reset() { + this._resetElementAttributes(this._element, 'overflow'); + this._resetElementAttributes(this._element, PROPERTY_PADDING); + this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING); + this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN); + } - if (includeScale && isHTMLElement(element)) { - scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1; - scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1; - } + isOverflowing() { + return this.getWidth() > 0; + } - var _ref = isElement(element) ? getWindow(element) : window, - visualViewport = _ref.visualViewport; - - var addVisualOffsets = !isLayoutViewport() && isFixedStrategy; - var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX; - var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY; - var width = clientRect.width / scaleX; - var height = clientRect.height / scaleY; - return { - width: width, - height: height, - top: y, - right: x + width, - bottom: y + height, - left: x, - x: x, - y: y - }; - } + // Private + _disableOverFlow() { + this._saveInitialAttribute(this._element, 'overflow'); + this._element.style.overflow = 'hidden'; + } - // means it doesn't take into account transforms. + _setElementAttributes(selector, styleProperty, callback) { + const scrollbarWidth = this.getWidth(); + const manipulationCallBack = element => { + if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { + return; + } + this._saveInitialAttribute(element, styleProperty); + const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty); + element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`); + }; + this._applyManipulationCallback(selector, manipulationCallBack); + } - function getLayoutRect(element) { - var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed. - // Fixes https://github.com/popperjs/popper-core/issues/1223 + _saveInitialAttribute(element, styleProperty) { + const actualValue = element.style.getPropertyValue(styleProperty); + if (actualValue) { + Manipulator.setDataAttribute(element, styleProperty, actualValue); + } + } - var width = element.offsetWidth; - var height = element.offsetHeight; + _resetElementAttributes(selector, styleProperty) { + const manipulationCallBack = element => { + const value = Manipulator.getDataAttribute(element, styleProperty); + // We only want to remove the property if the value is `null`; the value can also be zero + if (value === null) { + element.style.removeProperty(styleProperty); + return; + } + Manipulator.removeDataAttribute(element, styleProperty); + element.style.setProperty(styleProperty, value); + }; + this._applyManipulationCallback(selector, manipulationCallBack); + } - if (Math.abs(clientRect.width - width) <= 1) { - width = clientRect.width; + _applyManipulationCallback(selector, callBack) { + if (isElement$1(selector)) { + callBack(selector); + return; + } + for (const sel of SelectorEngine.find(selector, this._element)) { + callBack(sel); + } + } } - if (Math.abs(clientRect.height - height) <= 1) { - height = clientRect.height; - } + /** + * -------------------------------------------------------------------------- + * Bootstrap modal.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - return { - x: element.offsetLeft, - y: element.offsetTop, - width: width, - height: height - }; - } - function contains(parent, child) { - var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method + /** + * Constants + */ - if (parent.contains(child)) { - return true; - } // then fallback to custom implementation with Shadow DOM support - else if (rootNode && isShadowRoot(rootNode)) { - var next = child; + const NAME$7 = 'modal'; + const DATA_KEY$4 = 'bs.modal'; + const EVENT_KEY$4 = `.${DATA_KEY$4}`; + const DATA_API_KEY$2 = '.data-api'; + const ESCAPE_KEY$1 = 'Escape'; + const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`; + const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`; + const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`; + const EVENT_SHOW$4 = `show${EVENT_KEY$4}`; + const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`; + const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`; + const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`; + const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`; + const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`; + const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`; + const CLASS_NAME_OPEN = 'modal-open'; + const CLASS_NAME_FADE$3 = 'fade'; + const CLASS_NAME_SHOW$4 = 'show'; + const CLASS_NAME_STATIC = 'modal-static'; + const OPEN_SELECTOR$1 = '.modal.show'; + const SELECTOR_DIALOG = '.modal-dialog'; + const SELECTOR_MODAL_BODY = '.modal-body'; + const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle="modal"]'; + const Default$6 = { + backdrop: true, + focus: true, + keyboard: true + }; + const DefaultType$6 = { + backdrop: '(boolean|string)', + focus: 'boolean', + keyboard: 'boolean' + }; - do { - if (next && parent.isSameNode(next)) { - return true; - } // $FlowFixMe[prop-missing]: need a better way to handle this... + /** + * Class definition + */ + class Modal extends BaseComponent { + constructor(element, config) { + super(element, config); + this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); + this._backdrop = this._initializeBackDrop(); + this._focustrap = this._initializeFocusTrap(); + this._isShown = false; + this._isTransitioning = false; + this._scrollBar = new ScrollBarHelper(); + this._addEventListeners(); + } + + // Getters + static get Default() { + return Default$6; + } - next = next.parentNode || next.host; - } while (next); - } // Give up, the result is false + static get DefaultType() { + return DefaultType$6; + } + static get NAME() { + return NAME$7; + } - return false; - } + // Public + toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + } - function getComputedStyle$1(element) { - return getWindow(element).getComputedStyle(element); - } + show(relatedTarget) { + if (this._isShown || this._isTransitioning) { + return; + } + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, { + relatedTarget + }); + if (showEvent.defaultPrevented) { + return; + } + this._isShown = true; + this._isTransitioning = true; + this._scrollBar.hide(); + document.body.classList.add(CLASS_NAME_OPEN); + this._adjustDialog(); + this._backdrop.show(() => this._showElement(relatedTarget)); + } - function isTableElement(element) { - return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0; - } + hide() { + if (!this._isShown || this._isTransitioning) { + return; + } + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4); + if (hideEvent.defaultPrevented) { + return; + } + this._isShown = false; + this._isTransitioning = true; + this._focustrap.deactivate(); + this._element.classList.remove(CLASS_NAME_SHOW$4); + this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()); + } - function getDocumentElement(element) { - // $FlowFixMe[incompatible-return]: assume body is always available - return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing] - element.document) || window.document).documentElement; - } + dispose() { + EventHandler.off(window, EVENT_KEY$4); + EventHandler.off(this._dialog, EVENT_KEY$4); + this._backdrop.dispose(); + this._focustrap.deactivate(); + super.dispose(); + } - function getParentNode(element) { - if (getNodeName(element) === 'html') { - return element; - } + handleUpdate() { + this._adjustDialog(); + } - return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle - // $FlowFixMe[incompatible-return] - // $FlowFixMe[prop-missing] - element.assignedSlot || // step into the shadow DOM of the parent of a slotted node - element.parentNode || ( // DOM Element detected - isShadowRoot(element) ? element.host : null) || // ShadowRoot detected - // $FlowFixMe[incompatible-call]: HTMLElement is a Node - getDocumentElement(element) // fallback - - ); - } - - function getTrueOffsetParent(element) { - if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837 - getComputedStyle$1(element).position === 'fixed') { - return null; - } + // Private + _initializeBackDrop() { + return new Backdrop({ + isVisible: Boolean(this._config.backdrop), + // 'static' option will be translated to true, and booleans will keep their value, + isAnimated: this._isAnimated() + }); + } - return element.offsetParent; - } // `.offsetParent` reports `null` for fixed elements, while absolute elements - // return the containing block + _initializeFocusTrap() { + return new FocusTrap({ + trapElement: this._element + }); + } + _showElement(relatedTarget) { + // try to append dynamic modal + if (!document.body.contains(this._element)) { + document.body.append(this._element); + } + this._element.style.display = 'block'; + this._element.removeAttribute('aria-hidden'); + this._element.setAttribute('aria-modal', true); + this._element.setAttribute('role', 'dialog'); + this._element.scrollTop = 0; + const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog); + if (modalBody) { + modalBody.scrollTop = 0; + } + reflow(this._element); + this._element.classList.add(CLASS_NAME_SHOW$4); + const transitionComplete = () => { + if (this._config.focus) { + this._focustrap.activate(); + } + this._isTransitioning = false; + EventHandler.trigger(this._element, EVENT_SHOWN$4, { + relatedTarget + }); + }; + this._queueCallback(transitionComplete, this._dialog, this._isAnimated()); + } - function getContainingBlock(element) { - var isFirefox = /firefox/i.test(getUAString()); - var isIE = /Trident/i.test(getUAString()); + _addEventListeners() { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => { + if (event.key !== ESCAPE_KEY$1) { + return; + } + if (this._config.keyboard) { + this.hide(); + return; + } + this._triggerBackdropTransition(); + }); + EventHandler.on(window, EVENT_RESIZE$1, () => { + if (this._isShown && !this._isTransitioning) { + this._adjustDialog(); + } + }); + EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { + // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks + EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => { + if (this._element !== event.target || this._element !== event2.target) { + return; + } + if (this._config.backdrop === 'static') { + this._triggerBackdropTransition(); + return; + } + if (this._config.backdrop) { + this.hide(); + } + }); + }); + } - if (isIE && isHTMLElement(element)) { - // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport - var elementCss = getComputedStyle$1(element); + _hideModal() { + this._element.style.display = 'none'; + this._element.setAttribute('aria-hidden', true); + this._element.removeAttribute('aria-modal'); + this._element.removeAttribute('role'); + this._isTransitioning = false; + this._backdrop.hide(() => { + document.body.classList.remove(CLASS_NAME_OPEN); + this._resetAdjustments(); + this._scrollBar.reset(); + EventHandler.trigger(this._element, EVENT_HIDDEN$4); + }); + } - if (elementCss.position === 'fixed') { - return null; - } - } + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_FADE$3); + } - var currentNode = getParentNode(element); + _triggerBackdropTransition() { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1); + if (hideEvent.defaultPrevented) { + return; + } + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + const initialOverflowY = this._element.style.overflowY; + // return if the following background transition hasn't yet completed + if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { + return; + } + if (!isModalOverflowing) { + this._element.style.overflowY = 'hidden'; + } + this._element.classList.add(CLASS_NAME_STATIC); + this._queueCallback(() => { + this._element.classList.remove(CLASS_NAME_STATIC); + this._queueCallback(() => { + this._element.style.overflowY = initialOverflowY; + }, this._dialog); + }, this._dialog); + this._element.focus(); + } - if (isShadowRoot(currentNode)) { - currentNode = currentNode.host; - } + /** + * The following methods are used to handle overflowing modals + */ + + _adjustDialog() { + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + const scrollbarWidth = this._scrollBar.getWidth(); + const isBodyOverflowing = scrollbarWidth > 0; + if (isBodyOverflowing && !isModalOverflowing) { + const property = isRTL() ? 'paddingLeft' : 'paddingRight'; + this._element.style[property] = `${scrollbarWidth}px`; + } + if (!isBodyOverflowing && isModalOverflowing) { + const property = isRTL() ? 'paddingRight' : 'paddingLeft'; + this._element.style[property] = `${scrollbarWidth}px`; + } + } - while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) { - var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that - // create a containing block. - // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block + _resetAdjustments() { + this._element.style.paddingLeft = ''; + this._element.style.paddingRight = ''; + } - if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') { - return currentNode; - } else { - currentNode = currentNode.parentNode; - } + // Static + static jQueryInterface(config, relatedTarget) { + return this.each(function () { + const data = Modal.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](relatedTarget); + }); + } } - return null; - } // Gets the closest ancestor positioned element. Handles some edge cases, - // such as table ancestors and cross browser bugs. + /** + * Data API implementation + */ + EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + EventHandler.one(target, EVENT_SHOW$4, showEvent => { + if (showEvent.defaultPrevented) { + // only register focus restorer if modal will actually get shown + return; + } + EventHandler.one(target, EVENT_HIDDEN$4, () => { + if (isVisible(this)) { + this.focus(); + } + }); + }); - function getOffsetParent(element) { - var window = getWindow(element); - var offsetParent = getTrueOffsetParent(element); + // avoid conflict when clicking modal toggler while another one is open + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1); + if (alreadyOpen) { + Modal.getInstance(alreadyOpen).hide(); + } + const data = Modal.getOrCreateInstance(target); + data.toggle(this); + }); + enableDismissTrigger(Modal); - while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') { - offsetParent = getTrueOffsetParent(offsetParent); - } + /** + * jQuery + */ - if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static')) { - return window; - } + defineJQueryPlugin(Modal); - return offsetParent || getContainingBlock(element) || window; - } - - function getMainAxisFromPlacement(placement) { - return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y'; - } - - function within(min$1, value, max$1) { - return max(min$1, min(value, max$1)); - } - function withinMaxClamp(min, value, max) { - var v = within(min, value, max); - return v > max ? max : v; - } - - function getFreshSideObject() { - return { - top: 0, - right: 0, - bottom: 0, - left: 0 - }; - } - - function mergePaddingObject(paddingObject) { - return Object.assign({}, getFreshSideObject(), paddingObject); - } - - function expandToHashMap(value, keys) { - return keys.reduce(function (hashMap, key) { - hashMap[key] = value; - return hashMap; - }, {}); - } - - var toPaddingObject = function toPaddingObject(padding, state) { - padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, { - placement: state.placement - })) : padding; - return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); - }; - - function arrow(_ref) { - var _state$modifiersData$; - - var state = _ref.state, - name = _ref.name, - options = _ref.options; - var arrowElement = state.elements.arrow; - var popperOffsets = state.modifiersData.popperOffsets; - var basePlacement = getBasePlacement(state.placement); - var axis = getMainAxisFromPlacement(basePlacement); - var isVertical = [left, right].indexOf(basePlacement) >= 0; - var len = isVertical ? 'height' : 'width'; - - if (!arrowElement || !popperOffsets) { - return; - } + /** + * -------------------------------------------------------------------------- + * Bootstrap offcanvas.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - var paddingObject = toPaddingObject(options.padding, state); - var arrowRect = getLayoutRect(arrowElement); - var minProp = axis === 'y' ? top : left; - var maxProp = axis === 'y' ? bottom : right; - var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len]; - var startDiff = popperOffsets[axis] - state.rects.reference[axis]; - var arrowOffsetParent = getOffsetParent(arrowElement); - var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0; - var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is - // outside of the popper bounds - - var min = paddingObject[minProp]; - var max = clientSize - arrowRect[len] - paddingObject[maxProp]; - var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference; - var offset = within(min, center, max); // Prevents breaking syntax highlighting... - - var axisProp = axis; - state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$); - } - - function effect$1(_ref2) { - var state = _ref2.state, - options = _ref2.options; - var _options$element = options.element, - arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element; - - if (arrowElement == null) { - return; - } // CSS selector - - - if (typeof arrowElement === 'string') { - arrowElement = state.elements.popper.querySelector(arrowElement); - - if (!arrowElement) { - return; - } - } - if (!contains(state.elements.popper, arrowElement)) { - return; - } + /** + * Constants + */ - state.elements.arrow = arrowElement; - } // eslint-disable-next-line import/no-unused-modules - - - const arrow$1 = { - name: 'arrow', - enabled: true, - phase: 'main', - fn: arrow, - effect: effect$1, - requires: ['popperOffsets'], - requiresIfExists: ['preventOverflow'] - }; - - function getVariation(placement) { - return placement.split('-')[1]; - } - - var unsetSides = { - top: 'auto', - right: 'auto', - bottom: 'auto', - left: 'auto' - }; // Round the offsets to the nearest suitable subpixel based on the DPR. - // Zooming can change the DPR, but it seems to report a value that will - // cleanly divide the values into the appropriate subpixels. - - function roundOffsetsByDPR(_ref, win) { - var x = _ref.x, - y = _ref.y; - var dpr = win.devicePixelRatio || 1; - return { - x: round(x * dpr) / dpr || 0, - y: round(y * dpr) / dpr || 0 + const NAME$6 = 'offcanvas'; + const DATA_KEY$3 = 'bs.offcanvas'; + const EVENT_KEY$3 = `.${DATA_KEY$3}`; + const DATA_API_KEY$1 = '.data-api'; + const EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`; + const ESCAPE_KEY = 'Escape'; + const CLASS_NAME_SHOW$3 = 'show'; + const CLASS_NAME_SHOWING$1 = 'showing'; + const CLASS_NAME_HIDING = 'hiding'; + const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'; + const OPEN_SELECTOR = '.offcanvas.show'; + const EVENT_SHOW$3 = `show${EVENT_KEY$3}`; + const EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`; + const EVENT_HIDE$3 = `hide${EVENT_KEY$3}`; + const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`; + const EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`; + const EVENT_RESIZE = `resize${EVENT_KEY$3}`; + const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`; + const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`; + const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle="offcanvas"]'; + const Default$5 = { + backdrop: true, + keyboard: true, + scroll: false }; - } - - function mapToStyles(_ref2) { - var _Object$assign2; - - var popper = _ref2.popper, - popperRect = _ref2.popperRect, - placement = _ref2.placement, - variation = _ref2.variation, - offsets = _ref2.offsets, - position = _ref2.position, - gpuAcceleration = _ref2.gpuAcceleration, - adaptive = _ref2.adaptive, - roundOffsets = _ref2.roundOffsets, - isFixed = _ref2.isFixed; - var _offsets$x = offsets.x, - x = _offsets$x === void 0 ? 0 : _offsets$x, - _offsets$y = offsets.y, - y = _offsets$y === void 0 ? 0 : _offsets$y; - - var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({ - x: x, - y: y - }) : { - x: x, - y: y + const DefaultType$5 = { + backdrop: '(boolean|string)', + keyboard: 'boolean', + scroll: 'boolean' }; - x = _ref3.x; - y = _ref3.y; - var hasX = offsets.hasOwnProperty('x'); - var hasY = offsets.hasOwnProperty('y'); - var sideX = left; - var sideY = top; - var win = window; - - if (adaptive) { - var offsetParent = getOffsetParent(popper); - var heightProp = 'clientHeight'; - var widthProp = 'clientWidth'; - - if (offsetParent === getWindow(popper)) { - offsetParent = getDocumentElement(popper); - - if (getComputedStyle$1(offsetParent).position !== 'static' && position === 'absolute') { - heightProp = 'scrollHeight'; - widthProp = 'scrollWidth'; - } - } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it - - - offsetParent = offsetParent; - - if (placement === top || (placement === left || placement === right) && variation === end) { - sideY = bottom; - var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing] - offsetParent[heightProp]; - y -= offsetY - popperRect.height; - y *= gpuAcceleration ? 1 : -1; - } - - if (placement === left || (placement === top || placement === bottom) && variation === end) { - sideX = right; - var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing] - offsetParent[widthProp]; - x -= offsetX - popperRect.width; - x *= gpuAcceleration ? 1 : -1; - } - } + /** + * Class definition + */ + + class Offcanvas extends BaseComponent { + constructor(element, config) { + super(element, config); + this._isShown = false; + this._backdrop = this._initializeBackDrop(); + this._focustrap = this._initializeFocusTrap(); + this._addEventListeners(); + } - var commonStyles = Object.assign({ - position: position - }, adaptive && unsetSides); + // Getters + static get Default() { + return Default$5; + } - var _ref4 = roundOffsets === true ? roundOffsetsByDPR({ - x: x, - y: y - }, getWindow(popper)) : { - x: x, - y: y - }; + static get DefaultType() { + return DefaultType$5; + } - x = _ref4.x; - y = _ref4.y; + static get NAME() { + return NAME$6; + } - if (gpuAcceleration) { - var _Object$assign; + // Public + toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + } - return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign)); - } + show(relatedTarget) { + if (this._isShown) { + return; + } + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, { + relatedTarget + }); + if (showEvent.defaultPrevented) { + return; + } + this._isShown = true; + this._backdrop.show(); + if (!this._config.scroll) { + new ScrollBarHelper().hide(); + } + this._element.setAttribute('aria-modal', true); + this._element.setAttribute('role', 'dialog'); + this._element.classList.add(CLASS_NAME_SHOWING$1); + const completeCallBack = () => { + if (!this._config.scroll || this._config.backdrop) { + this._focustrap.activate(); + } + this._element.classList.add(CLASS_NAME_SHOW$3); + this._element.classList.remove(CLASS_NAME_SHOWING$1); + EventHandler.trigger(this._element, EVENT_SHOWN$3, { + relatedTarget + }); + }; + this._queueCallback(completeCallBack, this._element, true); + } - return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2)); - } - - function computeStyles(_ref5) { - var state = _ref5.state, - options = _ref5.options; - var _options$gpuAccelerat = options.gpuAcceleration, - gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat, - _options$adaptive = options.adaptive, - adaptive = _options$adaptive === void 0 ? true : _options$adaptive, - _options$roundOffsets = options.roundOffsets, - roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets; - var commonStyles = { - placement: getBasePlacement(state.placement), - variation: getVariation(state.placement), - popper: state.elements.popper, - popperRect: state.rects.popper, - gpuAcceleration: gpuAcceleration, - isFixed: state.options.strategy === 'fixed' - }; + hide() { + if (!this._isShown) { + return; + } + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3); + if (hideEvent.defaultPrevented) { + return; + } + this._focustrap.deactivate(); + this._element.blur(); + this._isShown = false; + this._element.classList.add(CLASS_NAME_HIDING); + this._backdrop.hide(); + const completeCallback = () => { + this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING); + this._element.removeAttribute('aria-modal'); + this._element.removeAttribute('role'); + if (!this._config.scroll) { + new ScrollBarHelper().reset(); + } + EventHandler.trigger(this._element, EVENT_HIDDEN$3); + }; + this._queueCallback(completeCallback, this._element, true); + } - if (state.modifiersData.popperOffsets != null) { - state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, { - offsets: state.modifiersData.popperOffsets, - position: state.options.strategy, - adaptive: adaptive, - roundOffsets: roundOffsets - }))); - } + dispose() { + this._backdrop.dispose(); + this._focustrap.deactivate(); + super.dispose(); + } - if (state.modifiersData.arrow != null) { - state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, { - offsets: state.modifiersData.arrow, - position: 'absolute', - adaptive: false, - roundOffsets: roundOffsets - }))); - } + // Private + _initializeBackDrop() { + const clickCallback = () => { + if (this._config.backdrop === 'static') { + EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); + return; + } + this.hide(); + }; + + // 'static' option will be translated to true, and booleans will keep their value + const isVisible = Boolean(this._config.backdrop); + return new Backdrop({ + className: CLASS_NAME_BACKDROP, + isVisible, + isAnimated: true, + rootElement: this._element.parentNode, + clickCallback: isVisible ? clickCallback : null + }); + } - state.attributes.popper = Object.assign({}, state.attributes.popper, { - 'data-popper-placement': state.placement - }); - } // eslint-disable-next-line import/no-unused-modules - - - const computeStyles$1 = { - name: 'computeStyles', - enabled: true, - phase: 'beforeWrite', - fn: computeStyles, - data: {} - }; - - var passive = { - passive: true - }; - - function effect(_ref) { - var state = _ref.state, - instance = _ref.instance, - options = _ref.options; - var _options$scroll = options.scroll, - scroll = _options$scroll === void 0 ? true : _options$scroll, - _options$resize = options.resize, - resize = _options$resize === void 0 ? true : _options$resize; - var window = getWindow(state.elements.popper); - var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper); - - if (scroll) { - scrollParents.forEach(function (scrollParent) { - scrollParent.addEventListener('scroll', instance.update, passive); - }); - } + _initializeFocusTrap() { + return new FocusTrap({ + trapElement: this._element + }); + } + + _addEventListeners() { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { + if (event.key !== ESCAPE_KEY) { + return; + } + if (this._config.keyboard) { + this.hide(); + return; + } + EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); + }); + } - if (resize) { - window.addEventListener('resize', instance.update, passive); + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Offcanvas.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](this); + }); + } } - return function () { - if (scroll) { - scrollParents.forEach(function (scrollParent) { - scrollParent.removeEventListener('scroll', instance.update, passive); + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + if (isDisabled(this)) { + return; + } + EventHandler.one(target, EVENT_HIDDEN$3, () => { + // focus on trigger when it is closed + if (isVisible(this)) { + this.focus(); + } }); - } - if (resize) { - window.removeEventListener('resize', instance.update, passive); - } - }; - } // eslint-disable-next-line import/no-unused-modules - - - const eventListeners = { - name: 'eventListeners', - enabled: true, - phase: 'write', - fn: function fn() {}, - effect: effect, - data: {} - }; - - var hash$1 = { - left: 'right', - right: 'left', - bottom: 'top', - top: 'bottom' - }; - function getOppositePlacement(placement) { - return placement.replace(/left|right|bottom|top/g, function (matched) { - return hash$1[matched]; + // avoid conflict when clicking a toggler of an offcanvas, while another is open + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR); + if (alreadyOpen && alreadyOpen !== target) { + Offcanvas.getInstance(alreadyOpen).hide(); + } + const data = Offcanvas.getOrCreateInstance(target); + data.toggle(this); }); - } - - var hash = { - start: 'end', - end: 'start' - }; - function getOppositeVariationPlacement(placement) { - return placement.replace(/start|end/g, function (matched) { - return hash[matched]; + EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => { + for (const selector of SelectorEngine.find(OPEN_SELECTOR)) { + Offcanvas.getOrCreateInstance(selector).show(); + } }); - } - - function getWindowScroll(node) { - var win = getWindow(node); - var scrollLeft = win.pageXOffset; - var scrollTop = win.pageYOffset; - return { - scrollLeft: scrollLeft, - scrollTop: scrollTop - }; - } - - function getWindowScrollBarX(element) { - // If has a CSS width greater than the viewport, then this will be - // incorrect for RTL. - // Popper 1 is broken in this case and never had a bug report so let's assume - // it's not an issue. I don't think anyone ever specifies width on - // anyway. - // Browsers where the left scrollbar doesn't cause an issue report `0` for - // this (e.g. Edge 2019, IE11, Safari) - return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft; - } - - function getViewportRect(element, strategy) { - var win = getWindow(element); - var html = getDocumentElement(element); - var visualViewport = win.visualViewport; - var width = html.clientWidth; - var height = html.clientHeight; - var x = 0; - var y = 0; - - if (visualViewport) { - width = visualViewport.width; - height = visualViewport.height; - var layoutViewport = isLayoutViewport(); - - if (layoutViewport || !layoutViewport && strategy === 'fixed') { - x = visualViewport.offsetLeft; - y = visualViewport.offsetTop; - } - } + EventHandler.on(window, EVENT_RESIZE, () => { + for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) { + if (getComputedStyle(element).position !== 'fixed') { + Offcanvas.getOrCreateInstance(element).hide(); + } + } + }); + enableDismissTrigger(Offcanvas); + + /** + * jQuery + */ + + defineJQueryPlugin(Offcanvas); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/sanitizer.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - return { - width: width, - height: height, - x: x + getWindowScrollBarX(element), - y: y + // js-docs-start allow-list + const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + const DefaultAllowlist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + dd: [], + div: [], + dl: [], + dt: [], + em: [], + hr: [], + h1: [], + h2: [], + h3: [], + h4: [], + h5: [], + h6: [], + i: [], + img: ['src', 'srcset', 'alt', 'title', 'width', 'height'], + li: [], + ol: [], + p: [], + pre: [], + s: [], + small: [], + span: [], + sub: [], + sup: [], + strong: [], + u: [], + ul: [] }; - } + // js-docs-end allow-list - // of the `` and `` rect bounds if horizontally scrollable + const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']); - function getDocumentRect(element) { - var _element$ownerDocumen; + /** + * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation + * contexts. + * + * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38 + */ + // eslint-disable-next-line unicorn/better-regex + const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i; + const allowedAttribute = (attribute, allowedAttributeList) => { + const attributeName = attribute.nodeName.toLowerCase(); + if (allowedAttributeList.includes(attributeName)) { + if (uriAttributes.has(attributeName)) { + return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue)); + } + return true; + } - var html = getDocumentElement(element); - var winScroll = getWindowScroll(element); - var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body; - var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0); - var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0); - var x = -winScroll.scrollLeft + getWindowScrollBarX(element); - var y = -winScroll.scrollTop; + // Check if a regular expression validates the attribute. + return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName)); + }; - if (getComputedStyle$1(body || html).direction === 'rtl') { - x += max(html.clientWidth, body ? body.clientWidth : 0) - width; + function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) { + if (!unsafeHtml.length) { + return unsafeHtml; + } + if (sanitizeFunction && typeof sanitizeFunction === 'function') { + return sanitizeFunction(unsafeHtml); + } + const domParser = new window.DOMParser(); + const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); + const elements = [].concat(...createdDocument.body.querySelectorAll('*')); + for (const element of elements) { + const elementName = element.nodeName.toLowerCase(); + if (!Object.keys(allowList).includes(elementName)) { + element.remove(); + continue; + } + const attributeList = [].concat(...element.attributes); + const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []); + for (const attribute of attributeList) { + if (!allowedAttribute(attribute, allowedAttributes)) { + element.removeAttribute(attribute.nodeName); + } + } + } + return createdDocument.body.innerHTML; } - return { - width: width, - height: height, - x: x, - y: y + /** + * -------------------------------------------------------------------------- + * Bootstrap util/template-factory.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$5 = 'TemplateFactory'; + const Default$4 = { + allowList: DefaultAllowlist, + content: {}, + // { selector : text , selector2 : text2 , } + extraClass: '', + html: false, + sanitize: true, + sanitizeFn: null, + template: '
' + }; + const DefaultType$4 = { + allowList: 'object', + content: 'object', + extraClass: '(string|function)', + html: 'boolean', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + template: 'string' + }; + const DefaultContentType = { + entry: '(string|element|function|null)', + selector: '(string|element)' }; - } - - function isScrollParent(element) { - // Firefox wants us to check `-x` and `-y` variations as well - var _getComputedStyle = getComputedStyle$1(element), - overflow = _getComputedStyle.overflow, - overflowX = _getComputedStyle.overflowX, - overflowY = _getComputedStyle.overflowY; - - return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX); - } - - function getScrollParent(node) { - if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) { - // $FlowFixMe[incompatible-return]: assume body is always available - return node.ownerDocument.body; - } - if (isHTMLElement(node) && isScrollParent(node)) { - return node; - } + /** + * Class definition + */ - return getScrollParent(getParentNode(node)); - } + class TemplateFactory extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + } - /* - given a DOM element, return the list of all scroll parents, up the list of ancesors - until we get to the top window object. This list is what we attach scroll listeners - to, because if any of these parent elements scroll, we'll need to re-calculate the - reference element's position. - */ + // Getters + static get Default() { + return Default$4; + } + + static get DefaultType() { + return DefaultType$4; + } - function listScrollParents(element, list) { - var _element$ownerDocumen; + static get NAME() { + return NAME$5; + } - if (list === void 0) { - list = []; - } + // Public + getContent() { + return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean); + } - var scrollParent = getScrollParent(element); - var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body); - var win = getWindow(scrollParent); - var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent; - var updatedList = list.concat(target); - return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here - updatedList.concat(listScrollParents(getParentNode(target))); - } - - function rectToClientRect(rect) { - return Object.assign({}, rect, { - left: rect.x, - top: rect.y, - right: rect.x + rect.width, - bottom: rect.y + rect.height - }); - } - - function getInnerBoundingClientRect(element, strategy) { - var rect = getBoundingClientRect(element, false, strategy === 'fixed'); - rect.top = rect.top + element.clientTop; - rect.left = rect.left + element.clientLeft; - rect.bottom = rect.top + element.clientHeight; - rect.right = rect.left + element.clientWidth; - rect.width = element.clientWidth; - rect.height = element.clientHeight; - rect.x = rect.left; - rect.y = rect.top; - return rect; - } - - function getClientRectFromMixedType(element, clippingParent, strategy) { - return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element))); - } // A "clipping parent" is an overflowable container with the characteristic of - // clipping (or hiding) overflowing elements with a position different from - // `initial` - - - function getClippingParents(element) { - var clippingParents = listScrollParents(getParentNode(element)); - var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle$1(element).position) >= 0; - var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element; - - if (!isElement(clipperElement)) { - return []; - } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414 - - - return clippingParents.filter(function (clippingParent) { - return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body'; - }); - } // Gets the maximum area that the element is visible in due to any number of - // clipping parents - - - function getClippingRect(element, boundary, rootBoundary, strategy) { - var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary); - var clippingParents = [].concat(mainClippingParents, [rootBoundary]); - var firstClippingParent = clippingParents[0]; - var clippingRect = clippingParents.reduce(function (accRect, clippingParent) { - var rect = getClientRectFromMixedType(element, clippingParent, strategy); - accRect.top = max(rect.top, accRect.top); - accRect.right = min(rect.right, accRect.right); - accRect.bottom = min(rect.bottom, accRect.bottom); - accRect.left = max(rect.left, accRect.left); - return accRect; - }, getClientRectFromMixedType(element, firstClippingParent, strategy)); - clippingRect.width = clippingRect.right - clippingRect.left; - clippingRect.height = clippingRect.bottom - clippingRect.top; - clippingRect.x = clippingRect.left; - clippingRect.y = clippingRect.top; - return clippingRect; - } - - function computeOffsets(_ref) { - var reference = _ref.reference, - element = _ref.element, - placement = _ref.placement; - var basePlacement = placement ? getBasePlacement(placement) : null; - var variation = placement ? getVariation(placement) : null; - var commonX = reference.x + reference.width / 2 - element.width / 2; - var commonY = reference.y + reference.height / 2 - element.height / 2; - var offsets; - - switch (basePlacement) { - case top: - offsets = { - x: commonX, - y: reference.y - element.height - }; - break; + hasContent() { + return this.getContent().length > 0; + } - case bottom: - offsets = { - x: commonX, - y: reference.y + reference.height - }; - break; + changeContent(content) { + this._checkContent(content); + this._config.content = { + ...this._config.content, + ...content + }; + return this; + } - case right: - offsets = { - x: reference.x + reference.width, - y: commonY - }; - break; + toHtml() { + const templateWrapper = document.createElement('div'); + templateWrapper.innerHTML = this._maybeSanitize(this._config.template); + for (const [selector, text] of Object.entries(this._config.content)) { + this._setContent(templateWrapper, text, selector); + } + const template = templateWrapper.children[0]; + const extraClass = this._resolvePossibleFunction(this._config.extraClass); + if (extraClass) { + template.classList.add(...extraClass.split(' ')); + } + return template; + } - case left: - offsets = { - x: reference.x - element.width, - y: commonY - }; - break; + // Private + _typeCheckConfig(config) { + super._typeCheckConfig(config); + this._checkContent(config.content); + } - default: - offsets = { - x: reference.x, - y: reference.y - }; - } + _checkContent(arg) { + for (const [selector, content] of Object.entries(arg)) { + super._typeCheckConfig({ + selector, + entry: content + }, DefaultContentType); + } + } - var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null; + _setContent(template, content, selector) { + const templateElement = SelectorEngine.findOne(selector, template); + if (!templateElement) { + return; + } + content = this._resolvePossibleFunction(content); + if (!content) { + templateElement.remove(); + return; + } + if (isElement$1(content)) { + this._putElementInTemplate(getElement(content), templateElement); + return; + } + if (this._config.html) { + templateElement.innerHTML = this._maybeSanitize(content); + return; + } + templateElement.textContent = content; + } - if (mainAxis != null) { - var len = mainAxis === 'y' ? 'height' : 'width'; + _maybeSanitize(arg) { + return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg; + } - switch (variation) { - case start: - offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2); - break; + _resolvePossibleFunction(arg) { + return execute(arg, [this]); + } - case end: - offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2); - break; - } + _putElementInTemplate(element, templateElement) { + if (this._config.html) { + templateElement.innerHTML = ''; + templateElement.append(element); + return; + } + templateElement.textContent = element.textContent; + } } - return offsets; - } + /** + * -------------------------------------------------------------------------- + * Bootstrap tooltip.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - function detectOverflow(state, options) { - if (options === void 0) { - options = {}; - } - var _options = options, - _options$placement = _options.placement, - placement = _options$placement === void 0 ? state.placement : _options$placement, - _options$strategy = _options.strategy, - strategy = _options$strategy === void 0 ? state.strategy : _options$strategy, - _options$boundary = _options.boundary, - boundary = _options$boundary === void 0 ? clippingParents : _options$boundary, - _options$rootBoundary = _options.rootBoundary, - rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary, - _options$elementConte = _options.elementContext, - elementContext = _options$elementConte === void 0 ? popper : _options$elementConte, - _options$altBoundary = _options.altBoundary, - altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary, - _options$padding = _options.padding, - padding = _options$padding === void 0 ? 0 : _options$padding; - var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); - var altContext = elementContext === popper ? reference : popper; - var popperRect = state.rects.popper; - var element = state.elements[altBoundary ? altContext : elementContext]; - var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy); - var referenceClientRect = getBoundingClientRect(state.elements.reference); - var popperOffsets = computeOffsets({ - reference: referenceClientRect, - element: popperRect, - strategy: 'absolute', - placement: placement - }); - var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets)); - var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect - // 0 or negative = within the clipping rect - - var overflowOffsets = { - top: clippingClientRect.top - elementClientRect.top + paddingObject.top, - bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom, - left: clippingClientRect.left - elementClientRect.left + paddingObject.left, - right: elementClientRect.right - clippingClientRect.right + paddingObject.right + /** + * Constants + */ + + const NAME$4 = 'tooltip'; + const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']); + const CLASS_NAME_FADE$2 = 'fade'; + const CLASS_NAME_MODAL = 'modal'; + const CLASS_NAME_SHOW$2 = 'show'; + const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'; + const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`; + const EVENT_MODAL_HIDE = 'hide.bs.modal'; + const TRIGGER_HOVER = 'hover'; + const TRIGGER_FOCUS = 'focus'; + const TRIGGER_CLICK = 'click'; + const TRIGGER_MANUAL = 'manual'; + const EVENT_HIDE$2 = 'hide'; + const EVENT_HIDDEN$2 = 'hidden'; + const EVENT_SHOW$2 = 'show'; + const EVENT_SHOWN$2 = 'shown'; + const EVENT_INSERTED = 'inserted'; + const EVENT_CLICK$1 = 'click'; + const EVENT_FOCUSIN$1 = 'focusin'; + const EVENT_FOCUSOUT$1 = 'focusout'; + const EVENT_MOUSEENTER = 'mouseenter'; + const EVENT_MOUSELEAVE = 'mouseleave'; + const AttachmentMap = { + AUTO: 'auto', + TOP: 'top', + RIGHT: isRTL() ? 'left' : 'right', + BOTTOM: 'bottom', + LEFT: isRTL() ? 'right' : 'left' + }; + const Default$3 = { + allowList: DefaultAllowlist, + animation: true, + boundary: 'clippingParents', + container: false, + customClass: '', + delay: 0, + fallbackPlacements: ['top', 'right', 'bottom', 'left'], + html: false, + offset: [0, 6], + placement: 'top', + popperConfig: null, + sanitize: true, + sanitizeFn: null, + selector: false, + template: '', + title: '', + trigger: 'hover focus' + }; + const DefaultType$3 = { + allowList: 'object', + animation: 'boolean', + boundary: '(string|element)', + container: '(string|element|boolean)', + customClass: '(string|function)', + delay: '(number|object)', + fallbackPlacements: 'array', + html: 'boolean', + offset: '(array|string|function)', + placement: '(string|function)', + popperConfig: '(null|object|function)', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + selector: '(string|boolean)', + template: 'string', + title: '(string|element|function)', + trigger: 'string' }; - var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element - - if (elementContext === popper && offsetData) { - var offset = offsetData[placement]; - Object.keys(overflowOffsets).forEach(function (key) { - var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1; - var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x'; - overflowOffsets[key] += offset[axis] * multiply; - }); - } - return overflowOffsets; - } + /** + * Class definition + */ - function computeAutoPlacement(state, options) { - if (options === void 0) { - options = {}; - } + class Tooltip extends BaseComponent { + constructor(element, config) { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)'); + } + super(element, config); + + // Private + this._isEnabled = true; + this._timeout = 0; + this._isHovered = null; + this._activeTrigger = {}; + this._popper = null; + this._templateFactory = null; + this._newContent = null; + + // Protected + this.tip = null; + this._setListeners(); + if (!this._config.selector) { + this._fixTitle(); + } + } - var _options = options, - placement = _options.placement, - boundary = _options.boundary, - rootBoundary = _options.rootBoundary, - padding = _options.padding, - flipVariations = _options.flipVariations, - _options$allowedAutoP = _options.allowedAutoPlacements, - allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP; - var variation = getVariation(placement); - var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) { - return getVariation(placement) === variation; - }) : basePlacements; - var allowedPlacements = placements$1.filter(function (placement) { - return allowedAutoPlacements.indexOf(placement) >= 0; - }); + // Getters + static get Default() { + return Default$3; + } - if (allowedPlacements.length === 0) { - allowedPlacements = placements$1; - } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions... - - - var overflows = allowedPlacements.reduce(function (acc, placement) { - acc[placement] = detectOverflow(state, { - placement: placement, - boundary: boundary, - rootBoundary: rootBoundary, - padding: padding - })[getBasePlacement(placement)]; - return acc; - }, {}); - return Object.keys(overflows).sort(function (a, b) { - return overflows[a] - overflows[b]; - }); - } + static get DefaultType() { + return DefaultType$3; + } - function getExpandedFallbackPlacements(placement) { - if (getBasePlacement(placement) === auto) { - return []; - } + static get NAME() { + return NAME$4; + } - var oppositePlacement = getOppositePlacement(placement); - return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)]; - } + // Public + enable() { + this._isEnabled = true; + } - function flip(_ref) { - var state = _ref.state, - options = _ref.options, - name = _ref.name; + disable() { + this._isEnabled = false; + } - if (state.modifiersData[name]._skip) { - return; - } + toggleEnabled() { + this._isEnabled = !this._isEnabled; + } - var _options$mainAxis = options.mainAxis, - checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, - _options$altAxis = options.altAxis, - checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis, - specifiedFallbackPlacements = options.fallbackPlacements, - padding = options.padding, - boundary = options.boundary, - rootBoundary = options.rootBoundary, - altBoundary = options.altBoundary, - _options$flipVariatio = options.flipVariations, - flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio, - allowedAutoPlacements = options.allowedAutoPlacements; - var preferredPlacement = state.options.placement; - var basePlacement = getBasePlacement(preferredPlacement); - var isBasePlacement = basePlacement === preferredPlacement; - var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement)); - var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) { - return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, { - placement: placement, - boundary: boundary, - rootBoundary: rootBoundary, - padding: padding, - flipVariations: flipVariations, - allowedAutoPlacements: allowedAutoPlacements - }) : placement); - }, []); - var referenceRect = state.rects.reference; - var popperRect = state.rects.popper; - var checksMap = new Map(); - var makeFallbackChecks = true; - var firstFittingPlacement = placements[0]; - - for (var i = 0; i < placements.length; i++) { - var placement = placements[i]; - - var _basePlacement = getBasePlacement(placement); - - var isStartVariation = getVariation(placement) === start; - var isVertical = [top, bottom].indexOf(_basePlacement) >= 0; - var len = isVertical ? 'width' : 'height'; - var overflow = detectOverflow(state, { - placement: placement, - boundary: boundary, - rootBoundary: rootBoundary, - altBoundary: altBoundary, - padding: padding - }); - var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top; - - if (referenceRect[len] > popperRect[len]) { - mainVariationSide = getOppositePlacement(mainVariationSide); - } - - var altVariationSide = getOppositePlacement(mainVariationSide); - var checks = []; - - if (checkMainAxis) { - checks.push(overflow[_basePlacement] <= 0); - } - - if (checkAltAxis) { - checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0); - } - - if (checks.every(function (check) { - return check; - })) { - firstFittingPlacement = placement; - makeFallbackChecks = false; - break; - } - - checksMap.set(placement, checks); - } + toggle() { + if (!this._isEnabled) { + return; + } + this._activeTrigger.click = !this._activeTrigger.click; + if (this._isShown()) { + this._leave(); + return; + } + this._enter(); + } - if (makeFallbackChecks) { - // `2` may be desired in some cases – research later - var numberOfChecks = flipVariations ? 3 : 1; + dispose() { + clearTimeout(this._timeout); + EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); + if (this._element.getAttribute('data-bs-original-title')) { + this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title')); + } + this._disposePopper(); + super.dispose(); + } - var _loop = function _loop(_i) { - var fittingPlacement = placements.find(function (placement) { - var checks = checksMap.get(placement); + show() { + if (this._element.style.display === 'none') { + throw new Error('Please use show on visible elements'); + } + if (!(this._isWithContent() && this._isEnabled)) { + return; + } + const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2)); + const shadowRoot = findShadowRoot(this._element); + const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element); + if (showEvent.defaultPrevented || !isInTheDom) { + return; + } - if (checks) { - return checks.slice(0, _i).every(function (check) { - return check; - }); - } - }); + // TODO: v6 remove this or make it optional + this._disposePopper(); + const tip = this._getTipElement(); + this._element.setAttribute('aria-describedby', tip.getAttribute('id')); + const { + container + } = this._config; + if (!this._element.ownerDocument.documentElement.contains(this.tip)) { + container.append(tip); + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED)); + } + this._popper = this._createPopper(tip); + tip.classList.add(CLASS_NAME_SHOW$2); + + // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.on(element, 'mouseover', noop); + } + } + const complete = () => { + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2)); + if (this._isHovered === false) { + this._leave(); + } + this._isHovered = false; + }; + this._queueCallback(complete, this.tip, this._isAnimated()); + } - if (fittingPlacement) { - firstFittingPlacement = fittingPlacement; - return "break"; + hide() { + if (!this._isShown()) { + return; + } + const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2)); + if (hideEvent.defaultPrevented) { + return; + } + const tip = this._getTipElement(); + tip.classList.remove(CLASS_NAME_SHOW$2); + + // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.off(element, 'mouseover', noop); + } + } + this._activeTrigger[TRIGGER_CLICK] = false; + this._activeTrigger[TRIGGER_FOCUS] = false; + this._activeTrigger[TRIGGER_HOVER] = false; + this._isHovered = null; // it is a trick to support manual triggering + + const complete = () => { + if (this._isWithActiveTrigger()) { + return; + } + if (!this._isHovered) { + this._disposePopper(); + } + this._element.removeAttribute('aria-describedby'); + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2)); + }; + this._queueCallback(complete, this.tip, this._isAnimated()); } - }; - for (var _i = numberOfChecks; _i > 0; _i--) { - var _ret = _loop(_i); + update() { + if (this._popper) { + this._popper.update(); + } + } - if (_ret === "break") break; - } - } + // Protected + _isWithContent() { + return Boolean(this._getTitle()); + } - if (state.placement !== firstFittingPlacement) { - state.modifiersData[name]._skip = true; - state.placement = firstFittingPlacement; - state.reset = true; - } - } // eslint-disable-next-line import/no-unused-modules + _getTipElement() { + if (!this.tip) { + this.tip = this._createTipElement(this._newContent || this._getContentForTemplate()); + } + return this.tip; + } + _createTipElement(content) { + const tip = this._getTemplateFactory(content).toHtml(); - const flip$1 = { - name: 'flip', - enabled: true, - phase: 'main', - fn: flip, - requiresIfExists: ['offset'], - data: { - _skip: false - } - }; - - function getSideOffsets(overflow, rect, preventedOffsets) { - if (preventedOffsets === void 0) { - preventedOffsets = { - x: 0, - y: 0 - }; - } + // TODO: remove this check in v6 + if (!tip) { + return null; + } + tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); + // TODO: v6 the following can be achieved with CSS only + tip.classList.add(`bs-${this.constructor.NAME}-auto`); + const tipId = getUID(this.constructor.NAME).toString(); + tip.setAttribute('id', tipId); + if (this._isAnimated()) { + tip.classList.add(CLASS_NAME_FADE$2); + } + return tip; + } - return { - top: overflow.top - rect.height - preventedOffsets.y, - right: overflow.right - rect.width + preventedOffsets.x, - bottom: overflow.bottom - rect.height + preventedOffsets.y, - left: overflow.left - rect.width - preventedOffsets.x - }; - } + setContent(content) { + this._newContent = content; + if (this._isShown()) { + this._disposePopper(); + this.show(); + } + } - function isAnySideFullyClipped(overflow) { - return [top, right, bottom, left].some(function (side) { - return overflow[side] >= 0; - }); - } - - function hide(_ref) { - var state = _ref.state, - name = _ref.name; - var referenceRect = state.rects.reference; - var popperRect = state.rects.popper; - var preventedOffsets = state.modifiersData.preventOverflow; - var referenceOverflow = detectOverflow(state, { - elementContext: 'reference' - }); - var popperAltOverflow = detectOverflow(state, { - altBoundary: true - }); - var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect); - var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets); - var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets); - var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets); - state.modifiersData[name] = { - referenceClippingOffsets: referenceClippingOffsets, - popperEscapeOffsets: popperEscapeOffsets, - isReferenceHidden: isReferenceHidden, - hasPopperEscaped: hasPopperEscaped - }; - state.attributes.popper = Object.assign({}, state.attributes.popper, { - 'data-popper-reference-hidden': isReferenceHidden, - 'data-popper-escaped': hasPopperEscaped - }); - } // eslint-disable-next-line import/no-unused-modules - - - const hide$1 = { - name: 'hide', - enabled: true, - phase: 'main', - requiresIfExists: ['preventOverflow'], - fn: hide - }; - - function distanceAndSkiddingToXY(placement, rects, offset) { - var basePlacement = getBasePlacement(placement); - var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1; - - var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, { - placement: placement - })) : offset, - skidding = _ref[0], - distance = _ref[1]; - - skidding = skidding || 0; - distance = (distance || 0) * invertDistance; - return [left, right].indexOf(basePlacement) >= 0 ? { - x: distance, - y: skidding - } : { - x: skidding, - y: distance - }; - } - - function offset(_ref2) { - var state = _ref2.state, - options = _ref2.options, - name = _ref2.name; - var _options$offset = options.offset, - offset = _options$offset === void 0 ? [0, 0] : _options$offset; - var data = placements.reduce(function (acc, placement) { - acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset); - return acc; - }, {}); - var _data$state$placement = data[state.placement], - x = _data$state$placement.x, - y = _data$state$placement.y; - - if (state.modifiersData.popperOffsets != null) { - state.modifiersData.popperOffsets.x += x; - state.modifiersData.popperOffsets.y += y; - } + _getTemplateFactory(content) { + if (this._templateFactory) { + this._templateFactory.changeContent(content); + } else { + this._templateFactory = new TemplateFactory({ + ...this._config, + // the `content` var has to be after `this._config` + // to override config.content in case of popover + content, + extraClass: this._resolvePossibleFunction(this._config.customClass) + }); + } + return this._templateFactory; + } - state.modifiersData[name] = data; - } // eslint-disable-next-line import/no-unused-modules - - - const offset$1 = { - name: 'offset', - enabled: true, - phase: 'main', - requires: ['popperOffsets'], - fn: offset - }; - - function popperOffsets(_ref) { - var state = _ref.state, - name = _ref.name; - // Offsets are the actual position the popper needs to have to be - // properly positioned near its reference element - // This is the most basic placement, and will be adjusted by - // the modifiers in the next step - state.modifiersData[name] = computeOffsets({ - reference: state.rects.reference, - element: state.rects.popper, - strategy: 'absolute', - placement: state.placement - }); - } // eslint-disable-next-line import/no-unused-modules - - - const popperOffsets$1 = { - name: 'popperOffsets', - enabled: true, - phase: 'read', - fn: popperOffsets, - data: {} - }; - - function getAltAxis(axis) { - return axis === 'x' ? 'y' : 'x'; - } - - function preventOverflow(_ref) { - var state = _ref.state, - options = _ref.options, - name = _ref.name; - var _options$mainAxis = options.mainAxis, - checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, - _options$altAxis = options.altAxis, - checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis, - boundary = options.boundary, - rootBoundary = options.rootBoundary, - altBoundary = options.altBoundary, - padding = options.padding, - _options$tether = options.tether, - tether = _options$tether === void 0 ? true : _options$tether, - _options$tetherOffset = options.tetherOffset, - tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset; - var overflow = detectOverflow(state, { - boundary: boundary, - rootBoundary: rootBoundary, - padding: padding, - altBoundary: altBoundary - }); - var basePlacement = getBasePlacement(state.placement); - var variation = getVariation(state.placement); - var isBasePlacement = !variation; - var mainAxis = getMainAxisFromPlacement(basePlacement); - var altAxis = getAltAxis(mainAxis); - var popperOffsets = state.modifiersData.popperOffsets; - var referenceRect = state.rects.reference; - var popperRect = state.rects.popper; - var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, { - placement: state.placement - })) : tetherOffset; - var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? { - mainAxis: tetherOffsetValue, - altAxis: tetherOffsetValue - } : Object.assign({ - mainAxis: 0, - altAxis: 0 - }, tetherOffsetValue); - var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null; - var data = { - x: 0, - y: 0 - }; + _getContentForTemplate() { + return { + [SELECTOR_TOOLTIP_INNER]: this._getTitle() + }; + } - if (!popperOffsets) { - return; - } + _getTitle() { + return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title'); + } - if (checkMainAxis) { - var _offsetModifierState$; - - var mainSide = mainAxis === 'y' ? top : left; - var altSide = mainAxis === 'y' ? bottom : right; - var len = mainAxis === 'y' ? 'height' : 'width'; - var offset = popperOffsets[mainAxis]; - var min$1 = offset + overflow[mainSide]; - var max$1 = offset - overflow[altSide]; - var additive = tether ? -popperRect[len] / 2 : 0; - var minLen = variation === start ? referenceRect[len] : popperRect[len]; - var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go - // outside the reference bounds - - var arrowElement = state.elements.arrow; - var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : { - width: 0, - height: 0 - }; - var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject(); - var arrowPaddingMin = arrowPaddingObject[mainSide]; - var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want - // to include its full size in the calculation. If the reference is small - // and near the edge of a boundary, the popper can overflow even if the - // reference is not overflowing as well (e.g. virtual elements with no - // width or height) - - var arrowLen = within(0, referenceRect[len], arrowRect[len]); - var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis; - var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis; - var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow); - var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0; - var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0; - var tetherMin = offset + minOffset - offsetModifierValue - clientOffset; - var tetherMax = offset + maxOffset - offsetModifierValue; - var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1); - popperOffsets[mainAxis] = preventedOffset; - data[mainAxis] = preventedOffset - offset; - } + // Private + _initializeOnDelegatedTarget(event) { + return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig()); + } - if (checkAltAxis) { - var _offsetModifierState$2; + _isAnimated() { + return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2); + } - var _mainSide = mainAxis === 'x' ? top : left; + _isShown() { + return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2); + } - var _altSide = mainAxis === 'x' ? bottom : right; + _createPopper(tip) { + const placement = execute(this._config.placement, [this, tip, this._element]); + const attachment = AttachmentMap[placement.toUpperCase()]; + return createPopper(this._element, tip, this._getPopperConfig(attachment)); + } - var _offset = popperOffsets[altAxis]; + _getOffset() { + const { + offset + } = this._config; + if (typeof offset === 'string') { + return offset.split(',').map(value => Number.parseInt(value, 10)); + } + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element); + } + return offset; + } - var _len = altAxis === 'y' ? 'height' : 'width'; + _resolvePossibleFunction(arg) { + return execute(arg, [this._element]); + } - var _min = _offset + overflow[_mainSide]; + _getPopperConfig(attachment) { + const defaultBsPopperConfig = { + placement: attachment, + modifiers: [{ + name: 'flip', + options: { + fallbackPlacements: this._config.fallbackPlacements + } + }, { + name: 'offset', + options: { + offset: this._getOffset() + } + }, { + name: 'preventOverflow', + options: { + boundary: this._config.boundary + } + }, { + name: 'arrow', + options: { + element: `.${this.constructor.NAME}-arrow` + } + }, { + name: 'preSetPlacement', + enabled: true, + phase: 'beforeMain', + fn: data => { + // Pre-set Popper's placement attribute in order to read the arrow sizes properly. + // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement + this._getTipElement().setAttribute('data-popper-placement', data.state.placement); + } + }] + }; + return { + ...defaultBsPopperConfig, + ...execute(this._config.popperConfig, [defaultBsPopperConfig]) + }; + } - var _max = _offset - overflow[_altSide]; + _setListeners() { + const triggers = this._config.trigger.split(' '); + for (const trigger of triggers) { + if (trigger === 'click') { + EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context.toggle(); + }); + } else if (trigger !== TRIGGER_MANUAL) { + const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1); + const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1); + EventHandler.on(this._element, eventIn, this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true; + context._enter(); + }); + EventHandler.on(this._element, eventOut, this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget); + context._leave(); + }); + } + } + this._hideModalHandler = () => { + if (this._element) { + this.hide(); + } + }; + EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); + } - var isOriginSide = [top, left].indexOf(basePlacement) !== -1; + _fixTitle() { + const title = this._element.getAttribute('title'); + if (!title) { + return; + } + if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) { + this._element.setAttribute('aria-label', title); + } + this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility + this._element.removeAttribute('title'); + } - var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0; + _enter() { + if (this._isShown() || this._isHovered) { + this._isHovered = true; + return; + } + this._isHovered = true; + this._setTimeout(() => { + if (this._isHovered) { + this.show(); + } + }, this._config.delay.show); + } - var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis; + _leave() { + if (this._isWithActiveTrigger()) { + return; + } + this._isHovered = false; + this._setTimeout(() => { + if (!this._isHovered) { + this.hide(); + } + }, this._config.delay.hide); + } - var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max; + _setTimeout(handler, timeout) { + clearTimeout(this._timeout); + this._timeout = setTimeout(handler, timeout); + } - var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max); + _isWithActiveTrigger() { + return Object.values(this._activeTrigger).includes(true); + } - popperOffsets[altAxis] = _preventedOffset; - data[altAxis] = _preventedOffset - _offset; - } + _getConfig(config) { + const dataAttributes = Manipulator.getDataAttributes(this._element); + for (const dataAttribute of Object.keys(dataAttributes)) { + if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) { + delete dataAttributes[dataAttribute]; + } + } + config = { + ...dataAttributes, + ...(typeof config === 'object' && config ? config : {}) + }; + config = this._mergeConfigObj(config); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } - state.modifiersData[name] = data; - } // eslint-disable-next-line import/no-unused-modules + _configAfterMerge(config) { + config.container = config.container === false ? document.body : getElement(config.container); + if (typeof config.delay === 'number') { + config.delay = { + show: config.delay, + hide: config.delay + }; + } + if (typeof config.title === 'number') { + config.title = config.title.toString(); + } + if (typeof config.content === 'number') { + config.content = config.content.toString(); + } + return config; + } + _getDelegateConfig() { + const config = {}; + for (const [key, value] of Object.entries(this._config)) { + if (this.constructor.Default[key] !== value) { + config[key] = value; + } + } + config.selector = false; + config.trigger = 'manual'; - const preventOverflow$1 = { - name: 'preventOverflow', - enabled: true, - phase: 'main', - fn: preventOverflow, - requiresIfExists: ['offset'] - }; + // In the future can be replaced with: + // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]]) + // `Object.fromEntries(keysWithDifferentValues)` + return config; + } - function getHTMLElementScroll(element) { - return { - scrollLeft: element.scrollLeft, - scrollTop: element.scrollTop - }; - } + _disposePopper() { + if (this._popper) { + this._popper.destroy(); + this._popper = null; + } + if (this.tip) { + this.tip.remove(); + this.tip = null; + } + } - function getNodeScroll(node) { - if (node === getWindow(node) || !isHTMLElement(node)) { - return getWindowScroll(node); - } else { - return getHTMLElementScroll(node); + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Tooltip.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } } - } - function isElementScaled(element) { - var rect = element.getBoundingClientRect(); - var scaleX = round(rect.width) / element.offsetWidth || 1; - var scaleY = round(rect.height) / element.offsetHeight || 1; - return scaleX !== 1 || scaleY !== 1; - } // Returns the composite rect of an element relative to its offsetParent. - // Composite means it takes into account transforms as well as layout. + /** + * jQuery + */ + defineJQueryPlugin(Tooltip); - function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) { - if (isFixed === void 0) { - isFixed = false; - } + /** + * -------------------------------------------------------------------------- + * Bootstrap popover.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - var isOffsetParentAnElement = isHTMLElement(offsetParent); - var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent); - var documentElement = getDocumentElement(offsetParent); - var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed); - var scroll = { - scrollLeft: 0, - scrollTop: 0 - }; - var offsets = { - x: 0, - y: 0 - }; - if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { - if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078 - isScrollParent(documentElement)) { - scroll = getNodeScroll(offsetParent); - } - - if (isHTMLElement(offsetParent)) { - offsets = getBoundingClientRect(offsetParent, true); - offsets.x += offsetParent.clientLeft; - offsets.y += offsetParent.clientTop; - } else if (documentElement) { - offsets.x = getWindowScrollBarX(documentElement); - } - } + /** + * Constants + */ - return { - x: rect.left + scroll.scrollLeft - offsets.x, - y: rect.top + scroll.scrollTop - offsets.y, - width: rect.width, - height: rect.height + const NAME$3 = 'popover'; + const SELECTOR_TITLE = '.popover-header'; + const SELECTOR_CONTENT = '.popover-body'; + const Default$2 = { + ...Tooltip.Default, + content: '', + offset: [0, 8], + placement: 'right', + template: '', + trigger: 'click' + }; + const DefaultType$2 = { + ...Tooltip.DefaultType, + content: '(null|string|element|function)' }; - } - - function order(modifiers) { - var map = new Map(); - var visited = new Set(); - var result = []; - modifiers.forEach(function (modifier) { - map.set(modifier.name, modifier); - }); // On visiting object, check for its dependencies and visit them recursively - - function sort(modifier) { - visited.add(modifier.name); - var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []); - requires.forEach(function (dep) { - if (!visited.has(dep)) { - var depModifier = map.get(dep); - - if (depModifier) { - sort(depModifier); - } - } - }); - result.push(modifier); - } - modifiers.forEach(function (modifier) { - if (!visited.has(modifier.name)) { - // check for visited object - sort(modifier); - } - }); - return result; - } + /** + * Class definition + */ - function orderModifiers(modifiers) { - // order based on dependencies - var orderedModifiers = order(modifiers); // order based on phase + class Popover extends Tooltip { + // Getters + static get Default() { + return Default$2; + } - return modifierPhases.reduce(function (acc, phase) { - return acc.concat(orderedModifiers.filter(function (modifier) { - return modifier.phase === phase; - })); - }, []); - } - - function debounce(fn) { - var pending; - return function () { - if (!pending) { - pending = new Promise(function (resolve) { - Promise.resolve().then(function () { - pending = undefined; - resolve(fn()); - }); - }); - } + static get DefaultType() { + return DefaultType$2; + } - return pending; - }; - } - - function mergeByName(modifiers) { - var merged = modifiers.reduce(function (merged, current) { - var existing = merged[current.name]; - merged[current.name] = existing ? Object.assign({}, existing, current, { - options: Object.assign({}, existing.options, current.options), - data: Object.assign({}, existing.data, current.data) - }) : current; - return merged; - }, {}); // IE11 does not support Object.values - - return Object.keys(merged).map(function (key) { - return merged[key]; - }); - } + static get NAME() { + return NAME$3; + } - var DEFAULT_OPTIONS = { - placement: 'bottom', - modifiers: [], - strategy: 'absolute' - }; + // Overrides + _isWithContent() { + return this._getTitle() || this._getContent(); + } - function areValidElements() { - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } + // Private + _getContentForTemplate() { + return { + [SELECTOR_TITLE]: this._getTitle(), + [SELECTOR_CONTENT]: this._getContent() + }; + } - return !args.some(function (element) { - return !(element && typeof element.getBoundingClientRect === 'function'); - }); - } + _getContent() { + return this._resolvePossibleFunction(this._config.content); + } - function popperGenerator(generatorOptions) { - if (generatorOptions === void 0) { - generatorOptions = {}; + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Popover.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } } - var _generatorOptions = generatorOptions, - _generatorOptions$def = _generatorOptions.defaultModifiers, - defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def, - _generatorOptions$def2 = _generatorOptions.defaultOptions, - defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2; - return function createPopper(reference, popper, options) { - if (options === void 0) { - options = defaultOptions; - } - - var state = { - placement: 'bottom', - orderedModifiers: [], - options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions), - modifiersData: {}, - elements: { - reference: reference, - popper: popper - }, - attributes: {}, - styles: {} - }; - var effectCleanupFns = []; - var isDestroyed = false; - var instance = { - state: state, - setOptions: function setOptions(setOptionsAction) { - var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction; - cleanupModifierEffects(); - state.options = Object.assign({}, defaultOptions, state.options, options); - state.scrollParents = { - reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [], - popper: listScrollParents(popper) - }; // Orders the modifiers based on their dependencies and `phase` - // properties - - var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers - - state.orderedModifiers = orderedModifiers.filter(function (m) { - return m.enabled; - }); - runModifierEffects(); - return instance.update(); - }, - // Sync update – it will always be executed, even if not necessary. This - // is useful for low frequency updates where sync behavior simplifies the - // logic. - // For high frequency updates (e.g. `resize` and `scroll` events), always - // prefer the async Popper#update method - forceUpdate: function forceUpdate() { - if (isDestroyed) { - return; - } - - var _state$elements = state.elements, - reference = _state$elements.reference, - popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements - // anymore + /** + * jQuery + */ - if (!areValidElements(reference, popper)) { - return; - } // Store the reference and popper rects to be read by modifiers - - - state.rects = { - reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'), - popper: getLayoutRect(popper) - }; // Modifiers have the ability to reset the current update cycle. The - // most common use case for this is the `flip` modifier changing the - // placement, which then needs to re-run all the modifiers, because the - // logic was previously ran for the previous placement and is therefore - // stale/incorrect - - state.reset = false; - state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier - // is filled with the initial data specified by the modifier. This means - // it doesn't persist and is fresh on each update. - // To ensure persistent data, use `${name}#persistent` - - state.orderedModifiers.forEach(function (modifier) { - return state.modifiersData[modifier.name] = Object.assign({}, modifier.data); - }); - - for (var index = 0; index < state.orderedModifiers.length; index++) { - if (state.reset === true) { - state.reset = false; - index = -1; - continue; - } - - var _state$orderedModifie = state.orderedModifiers[index], - fn = _state$orderedModifie.fn, - _state$orderedModifie2 = _state$orderedModifie.options, - _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2, - name = _state$orderedModifie.name; - - if (typeof fn === 'function') { - state = fn({ - state: state, - options: _options, - name: name, - instance: instance - }) || state; - } - } - }, - // Async and optimistically optimized update – it will not be executed if - // not necessary (debounced to run at most once-per-tick) - update: debounce(function () { - return new Promise(function (resolve) { - instance.forceUpdate(); - resolve(state); - }); - }), - destroy: function destroy() { - cleanupModifierEffects(); - isDestroyed = true; - } - }; - - if (!areValidElements(reference, popper)) { - return instance; - } - - instance.setOptions(options).then(function (state) { - if (!isDestroyed && options.onFirstUpdate) { - options.onFirstUpdate(state); - } - }); // Modifiers have the ability to execute arbitrary code before the first - // update cycle runs. They will be executed in the same order as the update - // cycle. This is useful when a modifier adds some persistent data that - // other modifiers need to use, but the modifier is run after the dependent - // one. - - function runModifierEffects() { - state.orderedModifiers.forEach(function (_ref) { - var name = _ref.name, - _ref$options = _ref.options, - options = _ref$options === void 0 ? {} : _ref$options, - effect = _ref.effect; - - if (typeof effect === 'function') { - var cleanupFn = effect({ - state: state, - name: name, - instance: instance, - options: options - }); + defineJQueryPlugin(Popover); - var noopFn = function noopFn() {}; + /** + * -------------------------------------------------------------------------- + * Bootstrap scrollspy.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ - effectCleanupFns.push(cleanupFn || noopFn); - } - }); - } - function cleanupModifierEffects() { - effectCleanupFns.forEach(function (fn) { - return fn(); - }); - effectCleanupFns = []; - } + /** + * Constants + */ - return instance; + const NAME$2 = 'scrollspy'; + const DATA_KEY$2 = 'bs.scrollspy'; + const EVENT_KEY$2 = `.${DATA_KEY$2}`; + const DATA_API_KEY = '.data-api'; + const EVENT_ACTIVATE = `activate${EVENT_KEY$2}`; + const EVENT_CLICK = `click${EVENT_KEY$2}`; + const EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`; + const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; + const CLASS_NAME_ACTIVE$1 = 'active'; + const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]'; + const SELECTOR_TARGET_LINKS = '[href]'; + const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; + const SELECTOR_NAV_LINKS = '.nav-link'; + const SELECTOR_NAV_ITEMS = '.nav-item'; + const SELECTOR_LIST_ITEMS = '.list-group-item'; + const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`; + const SELECTOR_DROPDOWN = '.dropdown'; + const SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle'; + const Default$1 = { + offset: null, + // TODO: v6 @deprecated, keep it for backwards compatibility reasons + rootMargin: '0px 0px -25%', + smoothScroll: false, + target: null, + threshold: [0.1, 0.5, 1] + }; + const DefaultType$1 = { + offset: '(number|null)', + // TODO v6 @deprecated, keep it for backwards compatibility reasons + rootMargin: 'string', + smoothScroll: 'boolean', + target: 'element', + threshold: 'array' }; - } - var createPopper$2 = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules - - var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1]; - var createPopper$1 = /*#__PURE__*/popperGenerator({ - defaultModifiers: defaultModifiers$1 - }); // eslint-disable-next-line import/no-unused-modules - - var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1]; - var createPopper = /*#__PURE__*/popperGenerator({ - defaultModifiers: defaultModifiers - }); // eslint-disable-next-line import/no-unused-modules - - const Popper = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ - __proto__: null, - afterMain, - afterRead, - afterWrite, - applyStyles: applyStyles$1, - arrow: arrow$1, - auto, - basePlacements, - beforeMain, - beforeRead, - beforeWrite, - bottom, - clippingParents, - computeStyles: computeStyles$1, - createPopper, - createPopperBase: createPopper$2, - createPopperLite: createPopper$1, - detectOverflow, - end, - eventListeners, - flip: flip$1, - hide: hide$1, - left, - main, - modifierPhases, - offset: offset$1, - placements, - popper, - popperGenerator, - popperOffsets: popperOffsets$1, - preventOverflow: preventOverflow$1, - read, - reference, - right, - start, - top, - variationPlacements, - viewport, - write - }, Symbol.toStringTag, { value: 'Module' })); - - /** - * -------------------------------------------------------------------------- - * Bootstrap dropdown.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$a = 'dropdown'; - const DATA_KEY$6 = 'bs.dropdown'; - const EVENT_KEY$6 = `.${DATA_KEY$6}`; - const DATA_API_KEY$3 = '.data-api'; - const ESCAPE_KEY$2 = 'Escape'; - const TAB_KEY$1 = 'Tab'; - const ARROW_UP_KEY$1 = 'ArrowUp'; - const ARROW_DOWN_KEY$1 = 'ArrowDown'; - const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button - - const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`; - const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`; - const EVENT_SHOW$5 = `show${EVENT_KEY$6}`; - const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`; - const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`; - const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`; - const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`; - const CLASS_NAME_SHOW$6 = 'show'; - const CLASS_NAME_DROPUP = 'dropup'; - const CLASS_NAME_DROPEND = 'dropend'; - const CLASS_NAME_DROPSTART = 'dropstart'; - const CLASS_NAME_DROPUP_CENTER = 'dropup-center'; - const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'; - const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'; - const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`; - const SELECTOR_MENU = '.dropdown-menu'; - const SELECTOR_NAVBAR = '.navbar'; - const SELECTOR_NAVBAR_NAV = '.navbar-nav'; - const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; - const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'; - const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'; - const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'; - const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'; - const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'; - const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'; - const PLACEMENT_TOPCENTER = 'top'; - const PLACEMENT_BOTTOMCENTER = 'bottom'; - const Default$9 = { - autoClose: true, - boundary: 'clippingParents', - display: 'dynamic', - offset: [0, 2], - popperConfig: null, - reference: 'toggle' - }; - const DefaultType$9 = { - autoClose: '(boolean|string)', - boundary: '(string|element)', - display: 'string', - offset: '(array|string|function)', - popperConfig: '(null|object|function)', - reference: '(string|element|object)' - }; - - /** - * Class definition - */ - - class Dropdown extends BaseComponent { - constructor(element, config) { - super(element, config); - this._popper = null; - this._parent = this._element.parentNode; // dropdown wrapper - // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ - this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent); - this._inNavbar = this._detectNavbar(); - } - - // Getters - static get Default() { - return Default$9; - } - static get DefaultType() { - return DefaultType$9; - } - static get NAME() { - return NAME$a; - } - - // Public - toggle() { - return this._isShown() ? this.hide() : this.show(); - } - show() { - if (isDisabled(this._element) || this._isShown()) { - return; - } - const relatedTarget = { - relatedTarget: this._element - }; - const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget); - if (showEvent.defaultPrevented) { - return; - } - this._createPopper(); - - // If this is a touch-enabled device we add extra - // empty mouseover listeners to the body's immediate children; - // only needed because of broken event delegation on iOS - // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html - if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) { - for (const element of [].concat(...document.body.children)) { - EventHandler.on(element, 'mouseover', noop); - } - } - this._element.focus(); - this._element.setAttribute('aria-expanded', true); - this._menu.classList.add(CLASS_NAME_SHOW$6); - this._element.classList.add(CLASS_NAME_SHOW$6); - EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget); - } - hide() { - if (isDisabled(this._element) || !this._isShown()) { - return; - } - const relatedTarget = { - relatedTarget: this._element - }; - this._completeHide(relatedTarget); - } - dispose() { - if (this._popper) { - this._popper.destroy(); - } - super.dispose(); - } - update() { - this._inNavbar = this._detectNavbar(); - if (this._popper) { - this._popper.update(); - } - } - // Private - _completeHide(relatedTarget) { - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget); - if (hideEvent.defaultPrevented) { - return; - } - - // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - if ('ontouchstart' in document.documentElement) { - for (const element of [].concat(...document.body.children)) { - EventHandler.off(element, 'mouseover', noop); - } - } - if (this._popper) { - this._popper.destroy(); - } - this._menu.classList.remove(CLASS_NAME_SHOW$6); - this._element.classList.remove(CLASS_NAME_SHOW$6); - this._element.setAttribute('aria-expanded', 'false'); - Manipulator.removeDataAttribute(this._menu, 'popper'); - EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget); - } - _getConfig(config) { - config = super._getConfig(config); - if (typeof config.reference === 'object' && !isElement$1(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { - // Popper virtual elements require a getBoundingClientRect method - throw new TypeError(`${NAME$a.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); - } - return config; - } - _createPopper() { - if (typeof Popper === 'undefined') { - throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)'); - } - let referenceElement = this._element; - if (this._config.reference === 'parent') { - referenceElement = this._parent; - } else if (isElement$1(this._config.reference)) { - referenceElement = getElement(this._config.reference); - } else if (typeof this._config.reference === 'object') { - referenceElement = this._config.reference; - } - const popperConfig = this._getPopperConfig(); - this._popper = createPopper(referenceElement, this._menu, popperConfig); - } - _isShown() { - return this._menu.classList.contains(CLASS_NAME_SHOW$6); - } - _getPlacement() { - const parentDropdown = this._parent; - if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { - return PLACEMENT_RIGHT; - } - if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { - return PLACEMENT_LEFT; - } - if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) { - return PLACEMENT_TOPCENTER; - } - if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) { - return PLACEMENT_BOTTOMCENTER; - } - - // We need to trim the value because custom properties can also include spaces - const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'; - if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { - return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP; - } - return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM; - } - _detectNavbar() { - return this._element.closest(SELECTOR_NAVBAR) !== null; - } - _getOffset() { - const { - offset - } = this._config; - if (typeof offset === 'string') { - return offset.split(',').map(value => Number.parseInt(value, 10)); - } - if (typeof offset === 'function') { - return popperData => offset(popperData, this._element); - } - return offset; - } - _getPopperConfig() { - const defaultBsPopperConfig = { - placement: this._getPlacement(), - modifiers: [{ - name: 'preventOverflow', - options: { - boundary: this._config.boundary - } - }, { - name: 'offset', - options: { - offset: this._getOffset() - } - }] - }; - - // Disable Popper if we have a static display or Dropdown is in Navbar - if (this._inNavbar || this._config.display === 'static') { - Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove - defaultBsPopperConfig.modifiers = [{ - name: 'applyStyles', - enabled: false - }]; - } - return { - ...defaultBsPopperConfig, - ...execute(this._config.popperConfig, [defaultBsPopperConfig]) - }; - } - _selectMenuItem({ - key, - target - }) { - const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element)); - if (!items.length) { - return; - } - - // if target isn't included in items (e.g. when expanding the dropdown) - // allow cycling to get the last item in case key equals ARROW_UP_KEY - getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus(); - } + /** + * Class definition + */ - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Dropdown.getOrCreateInstance(this, config); - if (typeof config !== 'string') { - return; - } - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - }); - } - static clearMenus(event) { - if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) { - return; - } - const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN); - for (const toggle of openToggles) { - const context = Dropdown.getInstance(toggle); - if (!context || context._config.autoClose === false) { - continue; - } - const composedPath = event.composedPath(); - const isMenuTarget = composedPath.includes(context._menu); - if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { - continue; - } - - // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu - if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) { - continue; - } - const relatedTarget = { - relatedTarget: context._element - }; - if (event.type === 'click') { - relatedTarget.clickEvent = event; + class ScrollSpy extends BaseComponent { + constructor(element, config) { + super(element, config); + + // this._element is the observablesContainer and config.target the menu links wrapper + this._targetLinks = new Map(); + this._observableSections = new Map(); + this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element; + this._activeTarget = null; + this._observer = null; + this._previousScrollData = { + visibleEntryTop: 0, + parentScrollTop: 0 + }; + this.refresh(); // initialize } - context._completeHide(relatedTarget); - } - } - static dataApiKeydownHandler(event) { - // If not an UP | DOWN | ESCAPE key => not a dropdown command - // If input/textarea && if key is other than ESCAPE => not a dropdown command - - const isInput = /input|textarea/i.test(event.target.tagName); - const isEscapeEvent = event.key === ESCAPE_KEY$2; - const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key); - if (!isUpOrDownEvent && !isEscapeEvent) { - return; - } - if (isInput && !isEscapeEvent) { - return; - } - event.preventDefault(); - - // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ - const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode); - const instance = Dropdown.getOrCreateInstance(getToggleButton); - if (isUpOrDownEvent) { - event.stopPropagation(); - instance.show(); - instance._selectMenuItem(event); - return; - } - if (instance._isShown()) { - // else is escape and we check if it is shown - event.stopPropagation(); - instance.hide(); - getToggleButton.focus(); - } - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler); - EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler); - EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus); - EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus); - EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) { - event.preventDefault(); - Dropdown.getOrCreateInstance(this).toggle(); - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Dropdown); - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/backdrop.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$9 = 'backdrop'; - const CLASS_NAME_FADE$4 = 'fade'; - const CLASS_NAME_SHOW$5 = 'show'; - const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`; - const Default$8 = { - className: 'modal-backdrop', - clickCallback: null, - isAnimated: false, - isVisible: true, - // if false, we use the backdrop helper without adding any element to the dom - rootElement: 'body' // give the choice to place backdrop under different elements - }; - const DefaultType$8 = { - className: 'string', - clickCallback: '(function|null)', - isAnimated: 'boolean', - isVisible: 'boolean', - rootElement: '(element|string)' - }; - - /** - * Class definition - */ - - class Backdrop extends Config { - constructor(config) { - super(); - this._config = this._getConfig(config); - this._isAppended = false; - this._element = null; - } - // Getters - static get Default() { - return Default$8; - } - static get DefaultType() { - return DefaultType$8; - } - static get NAME() { - return NAME$9; - } + // Getters + static get Default() { + return Default$1; + } - // Public - show(callback) { - if (!this._config.isVisible) { - execute(callback); - return; - } - this._append(); - const element = this._getElement(); - if (this._config.isAnimated) { - reflow(element); - } - element.classList.add(CLASS_NAME_SHOW$5); - this._emulateAnimation(() => { - execute(callback); - }); - } - hide(callback) { - if (!this._config.isVisible) { - execute(callback); - return; - } - this._getElement().classList.remove(CLASS_NAME_SHOW$5); - this._emulateAnimation(() => { - this.dispose(); - execute(callback); - }); - } - dispose() { - if (!this._isAppended) { - return; - } - EventHandler.off(this._element, EVENT_MOUSEDOWN); - this._element.remove(); - this._isAppended = false; - } + static get DefaultType() { + return DefaultType$1; + } - // Private - _getElement() { - if (!this._element) { - const backdrop = document.createElement('div'); - backdrop.className = this._config.className; - if (this._config.isAnimated) { - backdrop.classList.add(CLASS_NAME_FADE$4); - } - this._element = backdrop; - } - return this._element; - } - _configAfterMerge(config) { - // use getElement() with the default "body" to get a fresh Element on each instantiation - config.rootElement = getElement(config.rootElement); - return config; - } - _append() { - if (this._isAppended) { - return; - } - const element = this._getElement(); - this._config.rootElement.append(element); - EventHandler.on(element, EVENT_MOUSEDOWN, () => { - execute(this._config.clickCallback); - }); - this._isAppended = true; - } - _emulateAnimation(callback) { - executeAfterTransition(callback, this._getElement(), this._config.isAnimated); - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/focustrap.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$8 = 'focustrap'; - const DATA_KEY$5 = 'bs.focustrap'; - const EVENT_KEY$5 = `.${DATA_KEY$5}`; - const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`; - const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`; - const TAB_KEY = 'Tab'; - const TAB_NAV_FORWARD = 'forward'; - const TAB_NAV_BACKWARD = 'backward'; - const Default$7 = { - autofocus: true, - trapElement: null // The element to trap focus inside of - }; - const DefaultType$7 = { - autofocus: 'boolean', - trapElement: 'element' - }; - - /** - * Class definition - */ - - class FocusTrap extends Config { - constructor(config) { - super(); - this._config = this._getConfig(config); - this._isActive = false; - this._lastTabNavDirection = null; - } + static get NAME() { + return NAME$2; + } - // Getters - static get Default() { - return Default$7; - } - static get DefaultType() { - return DefaultType$7; - } - static get NAME() { - return NAME$8; - } + // Public + refresh() { + this._initializeTargetsAndObservables(); + this._maybeEnableSmoothScroll(); + if (this._observer) { + this._observer.disconnect(); + } else { + this._observer = this._getNewObserver(); + } + for (const section of this._observableSections.values()) { + this._observer.observe(section); + } + } - // Public - activate() { - if (this._isActive) { - return; - } - if (this._config.autofocus) { - this._config.trapElement.focus(); - } - EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop - EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event)); - EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); - this._isActive = true; - } - deactivate() { - if (!this._isActive) { - return; - } - this._isActive = false; - EventHandler.off(document, EVENT_KEY$5); - } + dispose() { + this._observer.disconnect(); + super.dispose(); + } - // Private - _handleFocusin(event) { - const { - trapElement - } = this._config; - if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { - return; - } - const elements = SelectorEngine.focusableChildren(trapElement); - if (elements.length === 0) { - trapElement.focus(); - } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { - elements[elements.length - 1].focus(); - } else { - elements[0].focus(); - } - } - _handleKeydown(event) { - if (event.key !== TAB_KEY) { - return; - } - this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; - } - } + // Private + _configAfterMerge(config) { + // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case + config.target = getElement(config.target) || document.body; - /** - * -------------------------------------------------------------------------- - * Bootstrap util/scrollBar.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ + // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only + config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin; + if (typeof config.threshold === 'string') { + config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value)); + } + return config; + } + _maybeEnableSmoothScroll() { + if (!this._config.smoothScroll) { + return; + } - /** - * Constants - */ + // unregister any previous listeners + EventHandler.off(this._config.target, EVENT_CLICK); + EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => { + const observableSection = this._observableSections.get(event.target.hash); + if (observableSection) { + event.preventDefault(); + const root = this._rootElement || window; + const height = observableSection.offsetTop - this._element.offsetTop; + if (root.scrollTo) { + root.scrollTo({ + top: height, + behavior: 'smooth' + }); + return; + } + + // Chrome 60 doesn't support `scrollTo` + root.scrollTop = height; + } + }); + } - const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; - const SELECTOR_STICKY_CONTENT = '.sticky-top'; - const PROPERTY_PADDING = 'padding-right'; - const PROPERTY_MARGIN = 'margin-right'; + _getNewObserver() { + const options = { + root: this._rootElement, + threshold: this._config.threshold, + rootMargin: this._config.rootMargin + }; + return new IntersectionObserver(entries => this._observerCallback(entries), options); + } - /** - * Class definition - */ + // The logic of selection + _observerCallback(entries) { + const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`); + const activate = entry => { + this._previousScrollData.visibleEntryTop = entry.target.offsetTop; + this._process(targetElement(entry)); + }; + const parentScrollTop = (this._rootElement || document.documentElement).scrollTop; + const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop; + this._previousScrollData.parentScrollTop = parentScrollTop; + for (const entry of entries) { + if (!entry.isIntersecting) { + this._activeTarget = null; + this._clearActiveClass(targetElement(entry)); + continue; + } + const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; + // if we are scrolling down, pick the bigger offsetTop + if (userScrollsDown && entryIsLowerThanPrevious) { + activate(entry); + // if parent isn't scrolled, let's keep the first visible item, breaking the iteration + if (!parentScrollTop) { + return; + } + continue; + } + + // if we are scrolling up, pick the smallest offsetTop + if (!userScrollsDown && !entryIsLowerThanPrevious) { + activate(entry); + } + } + } - class ScrollBarHelper { - constructor() { - this._element = document.body; - } + _initializeTargetsAndObservables() { + this._targetLinks = new Map(); + this._observableSections = new Map(); + const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target); + for (const anchor of targetLinks) { + // ensure that the anchor has an id and is not disabled + if (!anchor.hash || isDisabled(anchor)) { + continue; + } + const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element); + + // ensure that the observableSection exists & is visible + if (isVisible(observableSection)) { + this._targetLinks.set(decodeURI(anchor.hash), anchor); + this._observableSections.set(anchor.hash, observableSection); + } + } + } - // Public - getWidth() { - // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes - const documentWidth = document.documentElement.clientWidth; - return Math.abs(window.innerWidth - documentWidth); - } - hide() { - const width = this.getWidth(); - this._disableOverFlow(); - // give padding to element to balance the hidden scrollbar width - this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); - // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth - this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width); - this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width); - } - reset() { - this._resetElementAttributes(this._element, 'overflow'); - this._resetElementAttributes(this._element, PROPERTY_PADDING); - this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING); - this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN); - } - isOverflowing() { - return this.getWidth() > 0; - } + _process(target) { + if (this._activeTarget === target) { + return; + } + this._clearActiveClass(this._config.target); + this._activeTarget = target; + target.classList.add(CLASS_NAME_ACTIVE$1); + this._activateParents(target); + EventHandler.trigger(this._element, EVENT_ACTIVATE, { + relatedTarget: target + }); + } - // Private - _disableOverFlow() { - this._saveInitialAttribute(this._element, 'overflow'); - this._element.style.overflow = 'hidden'; - } - _setElementAttributes(selector, styleProperty, callback) { - const scrollbarWidth = this.getWidth(); - const manipulationCallBack = element => { - if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { - return; - } - this._saveInitialAttribute(element, styleProperty); - const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty); - element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`); - }; - this._applyManipulationCallback(selector, manipulationCallBack); - } - _saveInitialAttribute(element, styleProperty) { - const actualValue = element.style.getPropertyValue(styleProperty); - if (actualValue) { - Manipulator.setDataAttribute(element, styleProperty, actualValue); - } - } - _resetElementAttributes(selector, styleProperty) { - const manipulationCallBack = element => { - const value = Manipulator.getDataAttribute(element, styleProperty); - // We only want to remove the property if the value is `null`; the value can also be zero - if (value === null) { - element.style.removeProperty(styleProperty); - return; - } - Manipulator.removeDataAttribute(element, styleProperty); - element.style.setProperty(styleProperty, value); - }; - this._applyManipulationCallback(selector, manipulationCallBack); - } - _applyManipulationCallback(selector, callBack) { - if (isElement$1(selector)) { - callBack(selector); - return; - } - for (const sel of SelectorEngine.find(selector, this._element)) { - callBack(sel); - } - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap modal.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$7 = 'modal'; - const DATA_KEY$4 = 'bs.modal'; - const EVENT_KEY$4 = `.${DATA_KEY$4}`; - const DATA_API_KEY$2 = '.data-api'; - const ESCAPE_KEY$1 = 'Escape'; - const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`; - const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`; - const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`; - const EVENT_SHOW$4 = `show${EVENT_KEY$4}`; - const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`; - const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`; - const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`; - const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`; - const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`; - const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`; - const CLASS_NAME_OPEN = 'modal-open'; - const CLASS_NAME_FADE$3 = 'fade'; - const CLASS_NAME_SHOW$4 = 'show'; - const CLASS_NAME_STATIC = 'modal-static'; - const OPEN_SELECTOR$1 = '.modal.show'; - const SELECTOR_DIALOG = '.modal-dialog'; - const SELECTOR_MODAL_BODY = '.modal-body'; - const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle="modal"]'; - const Default$6 = { - backdrop: true, - focus: true, - keyboard: true - }; - const DefaultType$6 = { - backdrop: '(boolean|string)', - focus: 'boolean', - keyboard: 'boolean' - }; - - /** - * Class definition - */ - - class Modal extends BaseComponent { - constructor(element, config) { - super(element, config); - this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); - this._backdrop = this._initializeBackDrop(); - this._focustrap = this._initializeFocusTrap(); - this._isShown = false; - this._isTransitioning = false; - this._scrollBar = new ScrollBarHelper(); - this._addEventListeners(); - } + _activateParents(target) { + // Activate dropdown parents + if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { + SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1); + return; + } + for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) { + // Set triggered links parents as active + // With both