diff --git a/Sources/SwiftWebUI/Modifiers/BackgroundModifier.swift b/Sources/SwiftWebUI/Modifiers/BackgroundModifier.swift index 0176e05..0ad3b88 100644 --- a/Sources/SwiftWebUI/Modifiers/BackgroundModifier.swift +++ b/Sources/SwiftWebUI/Modifiers/BackgroundModifier.swift @@ -36,8 +36,20 @@ public struct BackgroundModifier: ViewModifier { let child = context.currentBuilder.buildTree(for: view, in: context) context.deleteLastElementIDComponent() - return HTMLBackgroundNode(elementID: context.currentElementID, - value: value, content: child) + var styles: CSSStyles? { + switch ( value.color, value.cornerRadius ) { + case ( .some(let color), .some(let cornerRadius) ): + return [ .backgroundColor: color, .borderRadius: cornerRadius ] + case ( .some(let color), .none ): + return [ .backgroundColor: color ] + case ( .none, .some(let cornerRadius) ): + return [ .borderRadius: cornerRadius ] + case ( .none, .none ): + return nil + } + } + + return HTMLStylePatchingNode.patch(child, withStyles: styles ?? [:], andElementID: context.currentElementID) } } diff --git a/Sources/SwiftWebUI/Modifiers/ShadowModifier.swift b/Sources/SwiftWebUI/Modifiers/ShadowModifier.swift index 7fbde9b..f1d578d 100644 --- a/Sources/SwiftWebUI/Modifiers/ShadowModifier.swift +++ b/Sources/SwiftWebUI/Modifiers/ShadowModifier.swift @@ -19,7 +19,6 @@ public struct ShadowModifier: ViewModifier { let child = context.currentBuilder.buildTree(for: view, in: context) context.deleteLastElementIDComponent() - return HTMLShadowNode(elementID: context.currentElementID, - value: value, content: child) + return HTMLStylePatchingNode.patch(child, withStyles: [.boxShadow : value], andElementID: context.currentElementID) } } diff --git a/Sources/SwiftWebUI/VirtualDOM/Layout/HTMLStylePatchingNode.swift b/Sources/SwiftWebUI/VirtualDOM/Layout/HTMLStylePatchingNode.swift new file mode 100644 index 0000000..c2d6e4a --- /dev/null +++ b/Sources/SwiftWebUI/VirtualDOM/Layout/HTMLStylePatchingNode.swift @@ -0,0 +1,92 @@ +// +// File.swift +// +// +// Created by SamuelIH on 9/15/20. +// + +import Foundation + +/// A node used to patch styles on an existing tree. +/// +/// This node is generally used for "visual" modifiers such as `shadow` and `backgroundColor`, because these affect visual appearances and not frame layout. +/// Rather then directly initializing this struct, consider using the static `patch` function, as it will automatically hook in to existing style patching nodes: +/// +/// // from ShadowModifier.swift +/// return HTMLStylePatchingNode.patch +/// ( +/// child, +/// withStyles : [.boxShadow : value], +/// andElementID : context.currentElementID +/// ) +struct HTMLStylePatchingNode: HTMLWrappingNode { + + let elementID : ElementID + var value : CSSStyles + let content : HTMLTreeNode + + + /// Patches the existing node with the new styles provided + /// + /// This function will wrap the `node` with an `HTMLStylePatchingNode` and patch in the new styles. However, if the `node` is *already* an `HTMLStylePatchingNode`, it will attempt to combine the styles into one node. Please note that if the existing styles conflict with the new ones provided, this will create a **new** style patching node, and wrap it around the existing one. + /// - Parameters: + /// - node: The node to be wrapped. It may not be wrapped if it's already an `HTMLStylePatchingNode` + /// - withStyles: The styles to patch it with + /// - Returns: An `HTMLStylePatchingNode` either a new one or a merged, existing one. + static func patch(_ node: HTMLTreeNode, withStyles: CSSStyles, andElementID elementID: ElementID) -> HTMLStylePatchingNode { + + if var existingPatch = node as? HTMLStylePatchingNode { + if !existingPatch.value.keys.contains(where: withStyles.keys.contains) { + // no conflict between the two patch nodes + // now we merge them + existingPatch.value = existingPatch.value.merging(withStyles) { (_, new) in new } + return existingPatch + } + } + + return HTMLStylePatchingNode(elementID: elementID, value: withStyles, content: node) + } + + func nodeByApplyingNewContent(_ newContent: HTMLTreeNode) -> Self { + return HTMLStylePatchingNode(elementID: elementID, value: value, content: newContent) + } + + public func dump(nesting: Int) { + let indent = String(repeating: " ", count: nesting) + let info = + value.map { (k,v) in + return " \(k.rawValue)=\(v.cssStringValue)" + } + .joined() + + print("\(indent)") + content.dump(nesting: nesting + 1) + print("\(indent)") + } + + func generateHTML(into html: inout String) { + html += "