diff --git a/Sources/TokamakCore/Modifiers/HoverActionModifier.swift b/Sources/TokamakCore/Modifiers/HoverActionModifier.swift new file mode 100644 index 00000000..190d3f74 --- /dev/null +++ b/Sources/TokamakCore/Modifiers/HoverActionModifier.swift @@ -0,0 +1,31 @@ +// Copyright 2021 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/// Underscore is present in the name for SwiftUI compatibility. +public struct _HoverActionModifier: ViewModifier { + public var hover: ((Bool) -> ())? + + public typealias Body = Never +} + +extension ModifiedContent + where Content: View, Modifier == _HoverActionModifier +{ + var hover: ((Bool) -> ())? { modifier.hover } +} + +public extension View { + func onHover(perform action: ((Bool) -> ())?) -> some View { + modifier(_HoverActionModifier(hover: action)) + } +} diff --git a/Sources/TokamakDOM/Modifiers/ActionModifier.swift b/Sources/TokamakDOM/Modifiers/ActionModifier.swift new file mode 100644 index 00000000..4b1b477c --- /dev/null +++ b/Sources/TokamakDOM/Modifiers/ActionModifier.swift @@ -0,0 +1,38 @@ +// Copyright 2021 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import TokamakCore + +public protocol DOMActionModifier { + var listeners: [String: Listener] { get } +} + +extension ModifiedContent + where Content: AnyDynamicHTML, Modifier: DOMActionModifier +{ + // Merge listeners + var listeners: [String: Listener] { + var attr = content.listeners + for (key, val) in modifier.listeners { + if let prev = attr[key] { + attr[key] = { input in + val(input) + prev(input) + } + } + } + + return attr + } +} diff --git a/Sources/TokamakDOM/Modifiers/Hover.swift b/Sources/TokamakDOM/Modifiers/Hover.swift new file mode 100644 index 00000000..899cb9cd --- /dev/null +++ b/Sources/TokamakDOM/Modifiers/Hover.swift @@ -0,0 +1,26 @@ +// Copyright 2021 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import TokamakCore + +extension _HoverActionModifier: DOMActionModifier { + public var listeners: [String: Listener] { + [ + "mouseover": + { _ in hover?(true) }, + "mouseout": + { _ in hover?(false) }, + ] + } +} diff --git a/Sources/TokamakDOM/Modifiers/ModifiedContent.swift b/Sources/TokamakDOM/Modifiers/ModifiedContent.swift new file mode 100644 index 00000000..4624ce21 --- /dev/null +++ b/Sources/TokamakDOM/Modifiers/ModifiedContent.swift @@ -0,0 +1,25 @@ +// Copyright 2021 Tokamak contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import TokamakCore + +// TOOD: Add _AnyModifiedActionContent similar to TokamakStaticHTML/ModifiedContent.swift? +extension ModifiedContent: DOMPrimitive where Content: View, Modifier: DOMActionModifier { + public var renderedBody: AnyView { + // TODO: Combine DOM nodes when possible, rather than generating arbitrary new ones + AnyView(DynamicHTML("div", listeners: modifier.listeners) { + content + }) + } +} diff --git a/Tests/TokamakStaticHTMLTests/HTMLStrategy.swift b/Tests/TokamakStaticHTMLTests/HTMLStrategy.swift index 4714eb79..8cba5368 100644 --- a/Tests/TokamakStaticHTMLTests/HTMLStrategy.swift +++ b/Tests/TokamakStaticHTMLTests/HTMLStrategy.swift @@ -15,8 +15,8 @@ #if canImport(SnapshotTesting) import SnapshotTesting -extension Snapshotting where Value == String, Format == String { - public static let html = Snapshotting(pathExtension: "html", diffing: .lines) +public extension Snapshotting where Value == String, Format == String { + static let html = Snapshotting(pathExtension: "html", diffing: .lines) } #endif