diff --git a/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift b/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift index 5b3739bb..7b6e5956 100644 --- a/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift +++ b/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift @@ -256,13 +256,47 @@ public protocol PositionPointAttribute: Attribute { /// Vector { /// Rectangle { /// } - /// .positionPoint((50, 50)) + /// .position(x: 50, y: 50) /// } /// ``` - /// - Parameter point: The coodinates to position the shape. + /// + /// - Parameters: + /// - x: The horizontal coordinate to position the shape. + /// - y: The vertical coordinate to position the shape /// /// - Returns: The element - func positionPoint(_ point: (Int, Int)) -> Self + func position(x: Int, y: Int) -> Self + + /// Set the position of the shape. + /// + /// ```Swift + /// Vector { + /// Rectangle { + /// } + /// .position(x: 50.0, y: 50.0) + /// } + /// ``` + /// + /// - Parameters: + /// - x: The horizontal coordinate to position the shape. + /// - y: The vertical coordinate to position the shape + /// + /// - Returns: The element + func position(x: Double, y: Double) -> Self + + /// Set the position of the shape. + /// + /// ```Swift + /// Vector { + /// Rectangle { + /// } + /// .position(UnitPoint(x: 50, y: 50)) + /// } + /// ``` + /// - Parameter point: The coordinates to position the shape. + /// + /// - Returns: The element + func position(_ point: UnitPoint) -> Self } extension PositionPointAttribute where Self: ContentNode { @@ -283,6 +317,14 @@ extension PositionPointAttribute where Self: ContentNode { return .init(attributes: attributes, content: content) } + + internal func mutate(x value: String) -> Self { + return self.mutate(key: "x", value: value) + } + + internal func mutate(y value: String) -> Self { + return self.mutate(key: "y", value: value) + } } /// A type that provides the `radiusPoint` modifier. @@ -295,13 +337,47 @@ public protocol RadiusPointAttribute: Attribute { /// Vector { /// Rectangle { /// } - /// .radiusPoint((10, 10)) + /// .radius(x: 50, y: 50) /// } /// ``` - /// - Parameter point: The radius to apply to all corners. + /// + /// - Parameters: + /// - x: The horizontal coordinate to round off corner. + /// - y: The vertical coordinate to round off corner. + /// + /// - Returns: The element + func radius(x: Int, y: Int) -> Self + + /// Apply a corner radius to the shape. + /// + /// ```swift + /// Vector { + /// Rectangle { + /// } + /// .radius(x: 50, y: 50) + /// } + /// ``` + /// + /// - Parameters: + /// - x: The horizontal coordinate to round off corner. + /// - y: The vertical coordinate to round off corner. /// /// - Returns: The element - func radiusPoint(_ point: (Int, Int)) -> Self + func radius(x: Double, y: Double) -> Self + + /// Apply a corner radius to the shape. + /// + /// ```swift + /// Vector { + /// Rectangle { + /// } + /// .radius(UnitPoint: (x: 50, y: 50)) + /// } + /// ``` + /// - Parameter point: The radius to round off corners. + /// + /// - Returns: The element + func radius(_ point: UnitPoint) -> Self } extension RadiusPointAttribute where Self: ContentNode { @@ -322,6 +398,14 @@ extension RadiusPointAttribute where Self: ContentNode { return .init(attributes: attributes, content: content) } + + internal func mutate(rx value: String) -> Self { + return self.mutate(key: "rx", value: value) + } + + internal func mutate(ry value: String) -> Self { + return self.mutate(key: "ry", value: value) + } } /// A type that provides the `centerPoint` modifier. @@ -334,13 +418,49 @@ public protocol CenterPointAttribute: Attribute { /// Vector { /// Circle { /// } - /// .centerPoint((50, 50)) + /// .center(x: 50, y: 50) + /// } + /// ``` + /// + /// - Parameters: + /// - x: The horizontal coordinate to use as the center. + /// - y: The vertical coordinate to use as the center. + /// + /// - Returns: The element + + func center(x: Int, y: Int) -> Self + + /// Set the center point of the shape. + /// + /// ```swift + /// Vector { + /// Circle { + /// } + /// .center(x: 50.0, y: 50.0) + /// } + /// ``` + /// + /// - Parameters: + /// - x: The horizontal coordinate to use as the center. + /// - y: The vertical coordinate to use as the center. + /// + /// - Returns: The element + func center(x: Double, y: Double) -> Self + + + /// Set the center point of the shape. + /// + /// ```swift + /// Vector { + /// Circle { + /// } + /// .center(UnitPoint(x: 50, y: 50)) /// } /// ``` /// - Parameter point: The coordinates to use as the center. /// /// - Returns: The element - func centerPoint(_ point: (Int, Int)) -> Self + func center(_ point: UnitPoint) -> Self } extension CenterPointAttribute where Self: ContentNode { @@ -361,6 +481,14 @@ extension CenterPointAttribute where Self: ContentNode { return .init(attributes: attributes, content: content) } + + internal func mutate(cx value: String) -> Self { + return self.mutate(key: "cx", value: value) + } + + internal func mutate(cy value: String) -> Self { + return self.mutate(key: "cy", value: value) + } } /// A type that provides the `viewBox` modifier. @@ -368,17 +496,38 @@ extension CenterPointAttribute where Self: ContentNode { public protocol ViewBoxAttribute: Attribute { /// Set the view box for the vector. - /// + /// /// ```swift /// Vector { /// } - /// .viewBox("0 0 400 200") + /// .viewBox(x: 0, y: 0, width: 400, height: 200") /// ``` + /// + /// - Parameters: + /// - x: The horizontal coordinate to use for the origin. + /// - y: The vertical coordinate to use for the origin. + /// - width: The width of the viewport + /// - height: The height of the viewport + /// + /// - Returns: The element + func viewBox(x: Int, y: Int, width: Int, height: Int) -> Self + + /// Set the view box for the vector. /// - /// - Parameter value: The bounds used to define the viewport. - /// + /// ```swift + /// Vector { + /// } + /// .viewBox(x: 0.0, y: 0.0, width: 400.0, height: 200.0") + /// ``` + /// + /// - Parameters: + /// - x: The horizontal coordinate to use for the origin. + /// - y: The vertical coordinate to use for the origin. + /// - width: The width of the viewport + /// - height: The height of the viewport + /// /// - Returns: The element - func viewBox(_ value: String) -> Self + func viewBox(x: Double, y: Double, width: Double, height: Double) -> Self } extension ViewBoxAttribute where Self: ContentNode { diff --git a/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift b/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift index fe639c93..a285d160 100644 --- a/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift +++ b/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift @@ -22520,10 +22520,19 @@ extension Vector: GlobalVectorAttributes, WidthAttribute, HeightAttribute, ViewB return self.mutate(style: value) } + @available(*, deprecated, message: "Use the viewBox(x:y:width:height:) modifier instead.") public func viewBox(_ value: String) -> Vector { return self.mutate(viewbox: value) } + public func viewBox(x: Int, y: Int, width: Int, height: Int) -> Vector { + return self.mutate(viewbox: "\(x) \(y) \(width) \(height)") + } + + public func viewBox(x: Double, y: Double, width: Double, height: Double) -> Vector { + return self.mutate(viewbox: "\(x) \(y) \(width) \(height)") + } + public func fill(_ value: String) -> Vector { return self.mutate(fill: value) } diff --git a/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift b/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift index 05ee4324..6d2d67ec 100644 --- a/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift +++ b/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift @@ -87,10 +87,23 @@ extension Circle: GlobalVectorAttributes, CenterPointAttribute, RadiusAttribute return self.mutate(strokewidth: size) } + @available(*, deprecated, message: "Use the center(x:y:) modifier instead.") public func centerPoint(_ point: (Int, Int)) -> Circle { return self.mutate(centerpoint: point) } + public func center(x: Int, y: Int) -> Circle { + return self.mutate(cx: "\(x)").mutate(cy: "\(y)") + } + + public func center(x: Double, y: Double) -> Circle { + return self.mutate(cx: "\(x)").mutate(cy: "\(y)") + } + + public func center(_ point: UnitPoint) -> Circle { + return self.mutate(cx: point.x).mutate(cy: point.y) + } + public func radius(_ size: Int) -> Circle { return self.mutate(radius: size) } @@ -169,8 +182,8 @@ public struct Rectangle: ContentNode, VectorElement { } } -extension Rectangle: GlobalVectorAttributes, WidthAttribute, HeightAttribute, RadiusPointAttribute { - +extension Rectangle: GlobalVectorAttributes, WidthAttribute, HeightAttribute, RadiusPointAttribute, PositionPointAttribute { + public func id(_ value: String) -> Rectangle { return self.mutate(id: value) } @@ -199,10 +212,40 @@ extension Rectangle: GlobalVectorAttributes, WidthAttribute, HeightAttribute, Ra return self.mutate(strokewidth: size) } + @available(*, deprecated, message: "Use the radius(x:y:) modifier instead.") public func radiusPoint(_ point: (Int, Int)) -> Rectangle { return self.mutate(radiuspoint: point) } + public func radius(x: Int, y: Int) -> Rectangle { + return self.mutate(rx: "\(x)").mutate(ry: "\(y)") + } + + public func radius(x: Double, y: Double) -> Rectangle { + return self.mutate(rx: "\(x)").mutate(ry: "\(y)") + } + + public func radius(_ point: UnitPoint) -> Rectangle { + return self.mutate(rx: point.x).mutate(ry: point.y) + } + + @available(*, deprecated, message: "Use the position(x:y:) modifier instead.") + public func positionPoint(_ point: (Int, Int)) -> Rectangle { + return self.mutate(positionpoint: point) + } + + public func position(x: Int, y: Int) -> Rectangle { + return self.mutate(x: "\(x)").mutate(y: "\(y)") + } + + public func position(x: Double, y: Double) -> Rectangle { + return self.mutate(x: "\(x)").mutate(y: "\(y)") + } + + public func position(_ point: UnitPoint) -> Rectangle { + return self.mutate(x: point.x).mutate(y: point.y) + } + public func width(_ size: Int) -> Rectangle { return self.mutate(width: size) } @@ -315,14 +358,40 @@ extension Ellipse: GlobalVectorAttributes, CenterPointAttribute, RadiusPointAttr return self.mutate(strokewidth: size) } + @available(*, deprecated, message: "Use the center(x:y:) modifier instead.") public func centerPoint(_ point: (Int, Int)) -> Ellipse { return self.mutate(centerpoint: point) } + public func center(x: Int, y: Int) -> Ellipse { + return self.mutate(cx: "\(x)").mutate(cy: "\(y)") + } + + public func center(x: Double, y: Double) -> Ellipse { + return self.mutate(cx: "\(x)").mutate(cy: "\(y)") + } + + public func center(_ point: UnitPoint) -> Ellipse { + return self.mutate(cx: point.x).mutate(cy: point.y) + } + + @available(*, deprecated, message: "Use the radius(x:y:) modifier instead.") public func radiusPoint(_ point: (Int, Int)) -> Ellipse { return self.mutate(radiuspoint: point) } + public func radius(x: Int, y: Int) -> Ellipse { + return self.mutate(rx: "\(x)").mutate(ry: "\(y)") + } + + public func radius(x: Double, y: Double) -> Ellipse { + return self.mutate(rx: "\(x)").mutate(ry: "\(y)") + } + + public func radius(_ point: UnitPoint) -> Ellipse { + return self.mutate(rx: point.x).mutate(ry: point.y) + } + public func fillOpacity(_ value: Double) -> Ellipse { return self.mutate(fillopacity: value) } @@ -926,7 +995,7 @@ public struct Use: ContentNode, VectorElement { } } -extension Use: GlobalVectorAttributes, ReferenceAttribute, WidthAttribute, HeightAttribute { +extension Use: GlobalVectorAttributes, ReferenceAttribute, WidthAttribute, HeightAttribute, PositionPointAttribute { public func id(_ value: String) -> Use { return self.mutate(id: value) @@ -940,6 +1009,23 @@ extension Use: GlobalVectorAttributes, ReferenceAttribute, WidthAttribute, Heigh return self.mutate(href: value) } + @available(*, deprecated, message: "Use the position(x:y:) modifier instead.") + public func positionPoint(_ point: (Int, Int)) -> Use { + return self.mutate(positionpoint: point) + } + + public func position(x: Int, y: Int) -> Use { + return self.mutate(x: "\(x)").mutate(y: "\(y)") + } + + public func position(x: Double, y: Double) -> Use { + return self.mutate(x: "\(x)").mutate(y: "\(y)") + } + + public func position(_ point: UnitPoint) -> Use { + return self.mutate(x: point.x).mutate(y: point.y) + } + public func width(_ size: Int) -> Use { return self.mutate(width: size) } diff --git a/Sources/HTMLKit/Abstraction/Types/UnitPoint.swift b/Sources/HTMLKit/Abstraction/Types/UnitPoint.swift new file mode 100644 index 00000000..349b4c84 --- /dev/null +++ b/Sources/HTMLKit/Abstraction/Types/UnitPoint.swift @@ -0,0 +1,58 @@ +public struct UnitPoint { + + /// A enumeration of potential point formats + public enum PointFormat { + + /// Indicates an absolute value. + case absolute + + /// Indicates an relative value. + case relative + } + + /// The horizontal position of the point + internal var x: String + + /// The vertical position of the point + internal var y: String + + /// Create a point. + /// + /// - Parameters: + /// - x: The horizontal coordinate to place the point. + /// - y: The vertical coordinate to place the point. + /// - format: Whether the coordinates should be relative. + public init(x: Double, y: Double, format: PointFormat = .absolute) { + + if case format = .relative { + + self.x = "\(x)%" + self.y = "\(y)%" + + } else { + + self.x = "\(x)" + self.y = "\(y)" + } + } + + /// Create a point. + /// + /// - Parameters: + /// - x: The horizontal coordinate to place the point. + /// - y: The vertical coordinate to place the point. + /// - format: Whether the coordinates should be relative. + public init(x: Int, y: Int, format: PointFormat = .absolute) { + + if case format = .relative { + + self.x = "\(x)%" + self.y = "\(y)%" + + } else { + + self.x = "\(x)" + self.y = "\(y)" + } + } +} diff --git a/Tests/HTMLKitTests/AttributesTests.swift b/Tests/HTMLKitTests/AttributesTests.swift index 8d273769..053bab59 100644 --- a/Tests/HTMLKitTests/AttributesTests.swift +++ b/Tests/HTMLKitTests/AttributesTests.swift @@ -619,18 +619,62 @@ final class AttributesTests: XCTestCase { return self.mutate(positionpoint: point) } + func position(x: Int, y: Int) -> Tag { + return self.mutate(x: "\(x)").mutate(y: "\(y)") + } + + func position(x: Double, y: Double) -> Tag { + return self.mutate(x: "\(x)").mutate(y: "\(y)") + } + + func position(_ point: UnitPoint) -> Tag { + return self.mutate(x: point.x).mutate(y: point.y) + } + func radiusPoint(_ point: (Int, Int)) -> Tag { return self.mutate(radiuspoint: point) } + func radius(x: Int, y: Int) -> Tag { + return self.mutate(rx: "\(x)").mutate(ry: "\(y)") + } + + func radius(x: Double, y: Double) -> Tag { + return self.mutate(rx: "\(x)").mutate(ry: "\(y)") + } + + func radius(_ point: HTMLKit.UnitPoint) -> Tag { + return self.mutate(rx: point.x).mutate(ry: point.y) + } + func centerPoint(_ point: (Int, Int)) -> Tag { return self.mutate(centerpoint: point) } + func center(x: Int, y: Int) -> Tag { + return self.mutate(cx: "\(x)").mutate(cy: "\(y)") + } + + func center(x: Double, y: Double) -> Tag { + return self.mutate(cx: "\(x)").mutate(cy: "\(y)") + } + + func center(_ point: UnitPoint) -> Tag { + return self.mutate(cx: point.x).mutate(cy: point.y) + } + func viewBox(_ value: String) -> Tag { return self.mutate(viewbox: value) } + func viewBox(x: Int, y: Int, width: Int, height: Int) -> Tag { + return self.mutate(viewbox: "\(x) \(y) \(width) \(height)") + } + + func viewBox(x: Double, y: Double, width: Double, height: Double) -> Tag { + return self.mutate(viewbox: "\(x) \(y) \(width) \(height)") + } + func namespace(_ value: String) -> Tag { return self.mutate(namespace: value) } @@ -2891,15 +2935,21 @@ final class AttributesTests: XCTestCase { ) } - func testPositionPointAttribute() throws { + func testPositionAttribute() throws { let view = TestView { - Tag {}.positionPoint((10,10)) + Tag {}.position(x: 50, y: 50) + Tag {}.position(x: 50.0, y: 50.0) + Tag {}.position(UnitPoint(x: 50.0, y: 50.0)) + Tag {}.position(UnitPoint(x: 50, y: 50, format: .relative)) } XCTAssertEqual(try renderer.render(view: view), """ - + \ + \ + \ + """ ) } @@ -2907,12 +2957,18 @@ final class AttributesTests: XCTestCase { func testRadiusPointAttribute() throws { let view = TestView { - Tag {}.radiusPoint((10,10)) + Tag {}.radius(x: 10, y: 10) + Tag {}.radius(x: 10.0, y: 10.0) + Tag {}.radius(UnitPoint(x: 10.0, y: 10.0)) + Tag {}.radius(UnitPoint(x: 10, y: 10, format: .relative)) } XCTAssertEqual(try renderer.render(view: view), """ - + \ + \ + \ + """ ) } @@ -2920,12 +2976,18 @@ final class AttributesTests: XCTestCase { func testCenterPointAttribute() throws { let view = TestView { - Tag {}.centerPoint((10,10)) + Tag {}.center(x: 10, y: 10) + Tag {}.center(x: 10.0, y: 10.0) + Tag {}.center(UnitPoint(x: 10.0, y: 10.0)) + Tag {}.center(UnitPoint(x: 10, y: 10, format: .relative)) } XCTAssertEqual(try renderer.render(view: view), """ - + \ + \ + \ + """ ) } @@ -2933,12 +2995,14 @@ final class AttributesTests: XCTestCase { func testViewBoxAttribute() throws { let view = TestView { - Tag {}.viewBox("0 0 100 100") + Tag {}.viewBox(x: 0, y: 0, width: 100, height: 100) + Tag {}.viewBox(x: 0, y: 0, width: 100.0, height: 100.0) } XCTAssertEqual(try renderer.render(view: view), """ - + \ + """ ) }