diff --git a/Sources/LiveViewNative/Modifiers/Animations Modifiers/NamespaceContext.swift b/Sources/LiveViewNative/Modifiers/Animations Modifiers/NamespaceContext.swift index d52f85e04..bf862b1f9 100644 --- a/Sources/LiveViewNative/Modifiers/Animations Modifiers/NamespaceContext.swift +++ b/Sources/LiveViewNative/Modifiers/Animations Modifiers/NamespaceContext.swift @@ -30,8 +30,20 @@ struct NamespaceContext: View { @Namespace private var namespace @Environment(\.namespaces) private var namespaces + #if !os(iOS) + @Environment(\.resetFocus) private var resetFocus + #endif + var body: some View { context.buildChildren(of: element) .environment(\.namespaces, namespaces.merging([id: namespace], uniquingKeysWith: { $1 })) + #if !os(iOS) + .onReceive(context.coordinator.receiveEvent("reset_focus")) { event in + guard let namespace = event["namespace"] as? String, + namespace == id + else { return } + resetFocus(in: self.namespace) + } + #endif } } diff --git a/Sources/LiveViewNative/Modifiers/TransformEffectModifier.swift b/Sources/LiveViewNative/Modifiers/Drawing and Graphics Modifiers/TransformEffectModifier.swift similarity index 100% rename from Sources/LiveViewNative/Modifiers/TransformEffectModifier.swift rename to Sources/LiveViewNative/Modifiers/Drawing and Graphics Modifiers/TransformEffectModifier.swift diff --git a/Sources/LiveViewNative/Modifiers/Focus Modifiers/FocusScopeModifier.swift b/Sources/LiveViewNative/Modifiers/Focus Modifiers/FocusScopeModifier.swift new file mode 100644 index 000000000..6ec0c61be --- /dev/null +++ b/Sources/LiveViewNative/Modifiers/Focus Modifiers/FocusScopeModifier.swift @@ -0,0 +1,60 @@ +// +// FocusScopeModifier.swift +// LiveViewNative +// +// Created by Carson Katri on 5/18/2023. +// +import SwiftUI + +/// Associates an element hierarchy with a namespace. +/// +/// Use a ``NamespaceContext`` element to create a focus namespace. +/// Provide the `id` of the namespace to the ``namespace`` argument. +/// +/// ```html +/// +/// +/// Username +/// Password +/// +/// +/// ``` +/// +/// ## Arguments +/// * ``namespace`` +#if swift(>=5.8) +@_documentation(visibility: public) +#endif +@available(macOS 13.0, tvOS 14.0, watchOS 7.0, *) +struct FocusScopeModifier: ViewModifier, Decodable { + /// The namespace to associate this hierarchy with. + /// + /// Use a ``NamespaceContext`` element to create a namespace. + #if swift(>=5.8) + @_documentation(visibility: public) + #endif + private let namespace: String + + @Environment(\.namespaces) private var namespaces + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.namespace = try container.decode(String.self, forKey: .namespace) + } + + func body(content: Content) -> some View { + if let namespace = namespaces[namespace] { + content + #if !os(iOS) + .focusScope(namespace) + #endif + } else { + content + } + } + + enum CodingKeys: CodingKey { + case namespace + } +} diff --git a/Sources/LiveViewNative/Modifiers/Focus Modifiers/PrefersDefaultFocusModifier.swift b/Sources/LiveViewNative/Modifiers/Focus Modifiers/PrefersDefaultFocusModifier.swift new file mode 100644 index 000000000..4ecac4681 --- /dev/null +++ b/Sources/LiveViewNative/Modifiers/Focus Modifiers/PrefersDefaultFocusModifier.swift @@ -0,0 +1,78 @@ +// +// PrefersDefaultFocusModifier.swift +// LiveViewNative +// +// Created by Carson Katri on 5/16/2023. +// +import SwiftUI + +/// Gives default focus to the element in a ``NamespaceContext``. +/// +/// Use a ``NamespaceContext`` element to create a focus namespace. +/// Provide the `id` of the namespace to the ``namespace`` argument. +/// +/// ```html +/// +/// +/// Username +/// Password +/// +/// +/// +/// ``` +/// +/// Push the `reset_focus` event to set focus back to the preferred defaults. +/// +/// ```elixir +/// def handle_event("reset_focus", %{ "namespace" => namespace }, socket) do +/// {:noreply, push_event(socket, :reset_focus, %{ namespace: namespace })} +/// end +/// ``` +/// +/// ## Arguments +/// * ``prefersDefaultFocus`` +/// * ``namespace`` +#if swift(>=5.8) +@_documentation(visibility: public) +#endif +@available(macOS 13.0, tvOS 14.0, watchOS 7.0, *) +struct PrefersDefaultFocusModifier: ViewModifier, Decodable { + /// Enables/disables the effect of this modifier. Defaults to `true`. + #if swift(>=5.8) + @_documentation(visibility: public) + #endif + private let prefersDefaultFocus: Bool + + /// The namespace where this element prefers default focus. + /// + /// Use a ``NamespaceContext`` element to create a namespace. + #if swift(>=5.8) + @_documentation(visibility: public) + #endif + private let namespace: String + + @Environment(\.namespaces) private var namespaces + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.prefersDefaultFocus = try container.decode(Bool.self, forKey: .prefersDefaultFocus) + self.namespace = try container.decode(String.self, forKey: .namespace) + } + + func body(content: Content) -> some View { + if let namespace = namespaces[namespace] { + content + #if !os(iOS) + .prefersDefaultFocus(prefersDefaultFocus, in: namespace) + #endif + } else { + content + } + } + + enum CodingKeys: CodingKey { + case prefersDefaultFocus + case namespace + } +} diff --git a/lib/live_view_native_swift_ui/modifiers/focus/focus_scope.ex b/lib/live_view_native_swift_ui/modifiers/focus/focus_scope.ex new file mode 100644 index 000000000..9d16dbd3e --- /dev/null +++ b/lib/live_view_native_swift_ui/modifiers/focus/focus_scope.ex @@ -0,0 +1,9 @@ +defmodule LiveViewNativeSwiftUi.Modifiers.FocusScope do + use LiveViewNativePlatform.Modifier + + alias LiveViewNativeSwiftUi.Types.Namespace + + modifier_schema "focus_scope" do + field :namespace, Namespace + end +end diff --git a/lib/live_view_native_swift_ui/modifiers/focus/prefers_default_focus.ex b/lib/live_view_native_swift_ui/modifiers/focus/prefers_default_focus.ex new file mode 100644 index 000000000..be261260d --- /dev/null +++ b/lib/live_view_native_swift_ui/modifiers/focus/prefers_default_focus.ex @@ -0,0 +1,10 @@ +defmodule LiveViewNativeSwiftUi.Modifiers.PrefersDefaultFocus do + use LiveViewNativePlatform.Modifier + + alias LiveViewNativeSwiftUi.Types.Namespace + + modifier_schema "prefers_default_focus" do + field :prefers_default_focus, :boolean, default: true + field :namespace, Namespace + end +end