diff --git a/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift b/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift
index bf4b3a9e..8a7b2c83 100644
--- a/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift
+++ b/Sources/HTMLKit/Abstraction/Attributes/BasicAttributes.swift
@@ -47,13 +47,52 @@ public protocol AcceptAttribute: Attribute {
/// ```swift
/// Input()
/// .type(.file)
- /// .accept("image/png, image/jpeg")
+ /// .accept(["image/png", "image/jpeg"])
/// ```
///
- /// - Parameter value: The file types to pick from.
+ /// - Parameter specifiers: The file types to pick from.
///
/// - Returns: The element
- func accept(_ value: String) -> Self
+ func accept(_ specifiers: [String]) -> Self
+
+ /// Filter accepted file types for upload.
+ ///
+ /// ```swift
+ /// Input()
+ /// .type(.file)
+ /// .accept("image/png", "image/jpeg")
+ /// ```
+ ///
+ /// - Parameter specifiers: The file types to pick from.
+ ///
+ /// - Returns: The element
+ func accept(_ specifiers: String...) -> Self
+
+ /// Filter accepted file types for upload.
+ ///
+ /// ```swift
+ /// Input()
+ /// .type(.file)
+ /// .accept([.ogg, .mpeg])
+ /// ```
+ ///
+ /// - Parameter specifiers: The file types to pick from.
+ ///
+ /// - Returns: The element
+ func accept(_ specifiers: [Values.Media]) -> Self
+
+ /// Filter accepted file types for upload.
+ ///
+ /// ```swift
+ /// Input()
+ /// .type(.file)
+ /// .accept(.ogg, .mpeg)
+ /// ```
+ ///
+ /// - Parameter specifiers: The file types to pick from.
+ ///
+ /// - Returns: The element
+ func accept(_ specifiers: Values.Media...) -> Self
}
extension AcceptAttribute where Self: ContentNode {
@@ -2113,14 +2152,34 @@ public protocol MediaAttribute: Attribute {
///
/// ```swift
/// Link()
- /// .reference("https://...")
- /// .media("print")
+ /// .reference("...css")
+ /// .media([
+ /// MediaQuery(target: .screen, features: .orientation(.portrait)),
+ /// MediaQuery(target: .print, features: .resolution("300dpi"))
+ /// ])
/// ```
///
- /// - Parameter value: The media to be considered.
+ /// - Parameter queries: The media to be considered.
///
/// - Returns: The element
- func media(_ value: String) -> Self
+ func media(_ queries: [MediaQuery]) -> Self
+
+
+ /// Specify the media the ressource is optimized for.
+ ///
+ /// ```swift
+ /// Link()
+ /// .reference("...css")
+ /// .media(
+ /// MediaQuery(target: .screen, features: .orientation(.portrait)),
+ /// MediaQuery(target: .print, features: .resolution("300dpi"))
+ /// )
+ /// ```
+ ///
+ /// - Parameter queries: The media to be considered.
+ ///
+ /// - Returns: The element
+ func media(_ queries: MediaQuery...) -> Self
}
extension MediaAttribute where Self: ContentNode {
@@ -3139,29 +3198,31 @@ extension SizeAttribute where Self: EmptyNode {
@_documentation(visibility: internal)
public protocol SizesAttribute: Attribute {
+ associatedtype SizesValueType
+
/// Describe different sizes for different viewport sizes.
///
/// ```swift
/// Link()
- /// .sizes(16x16)
+ /// .sizes("16x16", "32x32")
/// ```
///
- /// - Parameter size: The sizes to take into consideration.
+ /// - Parameter candidates: The sizes to take into consideration.
///
/// - Returns: The element
- func sizes(_ size: Int) -> Self
+ func sizes(_ candidates: [SizesValueType]) -> Self
}
extension SizesAttribute where Self: ContentNode {
- internal func mutate(sizes value: Int) -> Self {
+ internal func mutate(sizes value: String) -> Self {
return self.mutate(key: "sizes", value: value)
}
}
extension SizesAttribute where Self: EmptyNode {
- internal func mutate(sizes value: Int) -> Self {
+ internal func mutate(sizes value: String) -> Self {
return self.mutate(key: "sizes", value: value)
}
}
@@ -4062,21 +4123,33 @@ extension LoadingAttribute where Self: EmptyNode {
@_documentation(visibility: internal)
public protocol SourceSetAttribute: Attribute {
- /// Set a source set for a picture element.
+ /// Define a set of sources for a picture element.
///
/// ```swift
/// Picture {
/// Source()
- /// .sourceSet("https://...")
+ /// .sourceSet([SourceCandidate("...webp", width: 1024), SourceCandidate("...webp", width: 1680)])
+ /// }
+ /// ```
+ ///
+ /// - Parameter candidates: The candidates to choose from.
+ ///
+ /// - Returns: The element.
+ func sourceSet(_ candidates: [SourceCandidate]) -> Self
+
+ /// Define a set of sources for a picture element.
+ ///
+ /// ```swift
+ /// Picture {
/// Source()
- /// .sourceSet("https://...")
+ /// .sourceSet(SourceCandidate("...webp", width: 1024), SourceCandidate("...webp", width: 1680))
/// }
/// ```
///
- /// - Parameter url: The url path to load from.
+ /// - Parameter candidates: The candidates to choose from.
///
/// - Returns: The element.
- func sourceSet(_ url: String) -> Self
+ func sourceSet(_ candidates: SourceCandidate...) -> Self
}
extension SourceSetAttribute where Self: ContentNode {
diff --git a/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift b/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift
index 5b3739bb..68661a90 100644
--- a/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift
+++ b/Sources/HTMLKit/Abstraction/Attributes/VectorAttributes.swift
@@ -256,32 +256,57 @@ 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 {
- internal func mutate(positionpoint: (Int, Int)) -> Self {
-
- guard var attributes = self.attributes else {
-
- var attributes = OrderedDictionary()
- attributes["x"] = positionpoint.0
- attributes["y"] = positionpoint.1
-
- return .init(attributes: attributes, content: content)
- }
-
- attributes["x"] = positionpoint.0
- attributes["y"] = positionpoint.1
-
- 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)
}
}
@@ -295,32 +320,57 @@ public protocol RadiusPointAttribute: Attribute {
/// Vector {
/// Rectangle {
/// }
- /// .radiusPoint((10, 10))
+ /// .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 radius(x: Int, y: Int) -> Self
+
+ /// Apply a corner radius to the shape.
+ ///
+ /// ```swift
+ /// Vector {
+ /// Rectangle {
+ /// }
+ /// .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 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 coordinates to round off corners.
+ ///
+ /// - Returns: The element
+ func radius(_ point: UnitPoint) -> Self
}
extension RadiusPointAttribute where Self: ContentNode {
- internal func mutate(radiuspoint: (Int, Int)) -> Self {
-
- guard var attributes = self.attributes else {
-
- var attributes = OrderedDictionary()
- attributes["rx"] = radiuspoint.0
- attributes["ry"] = radiuspoint.1
-
- return .init(attributes: attributes, content: content)
- }
-
- attributes["rx"] = radiuspoint.0
- attributes["ry"] = radiuspoint.1
-
- 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)
}
}
@@ -334,32 +384,59 @@ 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 {
- internal func mutate(centerpoint: (Int, Int)) -> Self {
-
- guard var attributes = self.attributes else {
-
- var attributes = OrderedDictionary()
- attributes["cx"] = centerpoint.0
- attributes["cy"] = centerpoint.1
-
- return .init(attributes: attributes, content: content)
- }
-
- attributes["cx"] = centerpoint.0
- attributes["cy"] = centerpoint.1
-
- 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)
}
}
@@ -368,17 +445,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 f040a31b..5e764d6c 100644
--- a/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift
+++ b/Sources/HTMLKit/Abstraction/Elements/BodyElements.swift
@@ -7181,6 +7181,14 @@ extension Anchor: GlobalAttributes, GlobalEventAttributes, GlobalAriaAttributes,
return mutate(media: value)
}
+ public func media(_ queries: [MediaQuery]) -> Anchor {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func media(_ queries: MediaQuery...) -> Anchor {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
public func ping(_ value: String) -> Anchor {
return mutate(ping: value)
}
@@ -16870,12 +16878,25 @@ extension Image: GlobalAttributes, GlobalEventAttributes, GlobalAriaAttributes,
return mutate(source: value)
}
+ @available(*, deprecated, message: "Use the sourceSet(_:) modifier instead.")
public func sourceSet(_ value: String) -> Image {
return mutate(sourceset: value)
}
- public func sizes(_ size: Int) -> Image {
- return mutate(sizes: size)
+ public func sourceSet(_ candidates: [SourceCandidate]) -> Image {
+ return mutate(sourceset: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func sourceSet(_ candidates: SourceCandidate...) -> Image {
+ return mutate(sourceset: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func sizes(_ candidates: [SizeCandidate]) -> Image {
+ return mutate(sizes: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func sizes(_ candidates: SizeCandidate...) -> Image {
+ return mutate(sizes: candidates.map { $0.rawValue }.joined(separator: ", "))
}
public func width(_ size: Int) -> Image {
@@ -22520,10 +22541,19 @@ extension Vector: GlobalVectorAttributes, WidthAttribute, HeightAttribute, ViewB
return self.mutate(style: TaintedString(value, as: .css(.attribute)))
}
+ @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/FormElements.swift b/Sources/HTMLKit/Abstraction/Elements/FormElements.swift
index 867f6b69..3e88f3f8 100644
--- a/Sources/HTMLKit/Abstraction/Elements/FormElements.swift
+++ b/Sources/HTMLKit/Abstraction/Elements/FormElements.swift
@@ -187,9 +187,21 @@ extension Input: GlobalAttributes, GlobalEventAttributes, AcceptAttribute, Alter
return self
}
+
+ public func accept(_ specifiers: [String]) -> Input {
+ return mutate(accept: specifiers.joined(separator: ", "))
+ }
+
+ public func accept(_ specifiers: String...) -> Input {
+ return mutate(accept: specifiers.joined(separator: ", "))
+ }
+
+ public func accept(_ specifiers: [Values.Media]) -> Input {
+ return mutate(accept: specifiers.map { $0.rawValue }.joined(separator: ", "))
+ }
- public func accept(_ value: String) -> Input {
- return mutate(accept: value)
+ public func accept(_ specifiers: Values.Media...) -> Input {
+ return mutate(accept: specifiers.map { $0.rawValue }.joined(separator: ", "))
}
@_disfavoredOverload
diff --git a/Sources/HTMLKit/Abstraction/Elements/HeadElements.swift b/Sources/HTMLKit/Abstraction/Elements/HeadElements.swift
index 49c495f5..8be51e31 100644
--- a/Sources/HTMLKit/Abstraction/Elements/HeadElements.swift
+++ b/Sources/HTMLKit/Abstraction/Elements/HeadElements.swift
@@ -899,6 +899,14 @@ extension Style: GlobalAttributes, GlobalEventAttributes, TypeAttribute, MediaAt
return mutate(media: value)
}
+ public func media(_ queries: [MediaQuery]) -> Style {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func media(_ queries: MediaQuery...) -> Style {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
public func blocking(_ value: Values.Blocking) -> Style {
return mutate(blocking: value.rawValue)
}
@@ -1143,6 +1151,14 @@ extension Link: GlobalAttributes, GlobalEventAttributes, ReferenceAttribute, Ref
return mutate(media: value)
}
+ public func media(_ queries: [MediaQuery]) -> Link {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func media(_ queries: MediaQuery...) -> Link {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
public func referrerPolicy(_ value: Values.Policy) -> Link {
return mutate(referrerpolicy: value.rawValue)
}
@@ -1151,8 +1167,12 @@ extension Link: GlobalAttributes, GlobalEventAttributes, ReferenceAttribute, Ref
return mutate(rel: value.rawValue)
}
- public func sizes(_ size: Int) -> Link {
- return mutate(sizes: size)
+ public func sizes(_ candidates: [String]) -> Link {
+ return mutate(sizes: candidates.map { $0 }.joined(separator: " "))
+ }
+
+ public func sizes(_ candidates: String...) -> Link {
+ return mutate(sizes: candidates.map { $0 }.joined(separator: " "))
}
public func type(_ value: Values.Media) -> Link {
diff --git a/Sources/HTMLKit/Abstraction/Elements/MediaElements.swift b/Sources/HTMLKit/Abstraction/Elements/MediaElements.swift
index 7ad995b1..99b0b7db 100644
--- a/Sources/HTMLKit/Abstraction/Elements/MediaElements.swift
+++ b/Sources/HTMLKit/Abstraction/Elements/MediaElements.swift
@@ -199,18 +199,39 @@ extension Source: GlobalAttributes, GlobalEventAttributes, TypeAttribute, Source
return mutate(source: value)
}
+ @available(*, deprecated, message: "Use the sourceSet(_:) modifier instead.")
public func sourceSet(_ value: String) -> Source {
return mutate(sourceset: value)
}
- public func sizes(_ size: Int) -> Source {
- return mutate(sizes: size)
+ public func sourceSet(_ candidates: [SourceCandidate]) -> Source {
+ return mutate(sourceset: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func sourceSet(_ candidates: SourceCandidate...) -> Source {
+ return mutate(sourceset: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func sizes(_ candidates: [SizeCandidate]) -> Source {
+ return mutate(sizes: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func sizes(_ candidates: SizeCandidate...) -> Source {
+ return mutate(sizes: candidates.map { $0.rawValue }.joined(separator: ", "))
}
public func media(_ value: String) -> Source {
return mutate(media: value)
}
+ public func media(_ queries: [MediaQuery]) -> Source {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ public func media(_ queries: MediaQuery...) -> Source {
+ return mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
public func width(_ size: Int) -> Source {
return mutate(width: size)
}
diff --git a/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift b/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift
index 60069f67..fcd58049 100644
--- a/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift
+++ b/Sources/HTMLKit/Abstraction/Elements/VectorElements.swift
@@ -87,8 +87,21 @@ 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)
+ return self.mutate(cx: "\(point.0)").mutate(cy: "\(point.1)")
+ }
+
+ 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 {
@@ -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,8 +212,38 @@ 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)
+ return self.mutate(rx: "\(point.0)").mutate(ry: "\(point.1)")
+ }
+
+ 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(x: "\(point.0)").mutate(y: "\(point.1)")
+ }
+
+ 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 {
@@ -315,12 +358,38 @@ 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)
+ return self.mutate(cx: "\(point.0)").mutate(cy: "\(point.1)")
+ }
+
+ 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)
+ return self.mutate(rx: "\(point.0)").mutate(ry: "\(point.1)")
+ }
+
+ 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 {
@@ -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(x: "\(point.0)").mutate(y: "\(point.1)")
+ }
+
+ 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/MediaQuery.swift b/Sources/HTMLKit/Abstraction/Types/MediaQuery.swift
new file mode 100644
index 00000000..6b7b5b64
--- /dev/null
+++ b/Sources/HTMLKit/Abstraction/Types/MediaQuery.swift
@@ -0,0 +1,172 @@
+/// A type that represents a media query.
+///
+/// The query is used to define the conditions under which the resource should then be applied.
+///
+/// ```swift
+/// Link()
+/// .reference("https://...")
+/// .media(
+/// MediaQuery(.screen, features: .orientation(.landscape)),
+/// MediaQuery(.print, features: .resolution("300dpi"))
+/// )
+/// ```
+public struct MediaQuery {
+
+ /// An enumeration of potential interface orientations.
+ public enum InterfaceOrientation {
+
+ /// Indicates a landscape orientation.
+ case landscape
+
+ /// Indicates a portrait orientation.
+ case portrait
+
+ internal var rawValue: String {
+
+ switch self {
+ case .landscape:
+ return "landscape"
+
+ case .portrait:
+ return "portrait"
+ }
+ }
+ }
+
+ /// An enumeration of potential media features.
+ public enum MediaFeature {
+
+ /// Specifies the minimum target width.
+ case minWidth(String)
+
+ /// Specifies the target width.
+ case width(String)
+
+ /// Specifies the maximum target width.
+ case maxWidth(String)
+
+ /// Specifies the minimum target height.
+ case minHeight(String)
+
+ /// Specifies the target height.
+ case height(String)
+
+ /// Specifies the maximum target height.
+ case maxHeight(String)
+
+ /// Specifies the aspect ratio.
+ case aspectRatio(String)
+
+ /// Specifies the interface orientation.
+ case orientation(InterfaceOrientation)
+
+ /// Specifies the minimum display resolution.
+ case minResolution(String)
+
+ /// Specifies the display resolution.
+ case resolution(String)
+
+ /// Specifies the maximum display resolution.
+ case maxResolution(String)
+
+ /// Specifies the color depth.
+ case color(Int?)
+
+ internal var rawValue: String {
+
+ switch self {
+ case .minWidth(let length):
+ return "(min-width: \(length))"
+
+ case .width(let length):
+ return "(width: \(length))"
+
+ case .maxWidth(let length):
+ return "(max-width: \(length))"
+
+ case .minHeight(let length):
+ return "(min-height: \(length))"
+
+ case .height(let length):
+ return "(height: \(length))"
+
+ case .maxHeight(let length):
+ return "(max-height: \(length))"
+
+ case .aspectRatio(let ratio):
+ return "(aspect-ratio: \(ratio))"
+
+ case .orientation(let orientation):
+ return "(orientation: \(orientation.rawValue))"
+
+ case .minResolution(let pixel):
+ return "(min-resolution: \(pixel))"
+
+ case .resolution(let pixel):
+ return "(resolution: \(pixel))"
+
+ case .maxResolution(let pixel):
+ return "(max-resolution: \(pixel))"
+
+ case .color(let depth):
+
+ if let depth {
+ return "(color: \(depth))"
+ }
+
+ return "(color)"
+ }
+ }
+ }
+
+ /// An enumeration of potential media devices.
+ public enum MediaTarget: String {
+
+ /// Matches all devices.
+ case all
+
+ /// Matches screen devices.
+ case screen
+
+ /// Matches printer devices.
+ case print
+ }
+
+ /// The target of the query.
+ internal let target: MediaTarget
+
+ /// The potential features of the query.
+ internal let features: [MediaFeature]?
+
+ /// The raw representation of the type.
+ internal var rawValue: String {
+
+ if let features = self.features {
+ return "\(target.rawValue) and \(features.map { $0.rawValue }.joined(separator: " and "))"
+ }
+
+ return "\(target.rawValue)"
+ }
+
+ /// Create a media query.
+ ///
+ /// - Parameters:
+ /// - target: The media to target.
+ /// - features: The features to match to the target.
+ public init(_ target: MediaTarget, features: [MediaFeature]? = nil) {
+
+ self.target = target
+ self.features = features
+ }
+
+ /// Create a media query.
+ ///
+ /// - Parameters:
+ /// - target: The media to target.
+ /// - features: The features to match to the target.
+ public init(_ target: MediaTarget, features: MediaFeature...) {
+
+ self.target = target
+ self.features = features
+ }
+}
diff --git a/Sources/HTMLKit/Abstraction/Types/SizeCandidate.swift b/Sources/HTMLKit/Abstraction/Types/SizeCandidate.swift
new file mode 100644
index 00000000..85e73242
--- /dev/null
+++ b/Sources/HTMLKit/Abstraction/Types/SizeCandidate.swift
@@ -0,0 +1,109 @@
+/// A type that represents a size candidate.
+///
+/// The candidate is used to define the conditions under which the size should then be applied.
+///
+/// ```swift
+/// Image()
+/// .source("...png")
+/// .sourceSet(..., ...)
+/// .sizes(
+/// SizeCandidate("100vw", conditions: .maxWidth("1680px")),
+/// SizeCandidate("80vw")
+/// )
+/// ```
+public struct SizeCandidate {
+
+ /// An enumeration of potential interface orientations.
+ public enum InterfaceOrientation {
+
+ /// Indicates a landscape orientation.
+ case landscape
+
+ /// Indicates a portrait orientation.
+ case portrait
+
+ /// The raw representation of the type.
+ internal var rawValue: String {
+
+ switch self {
+ case .landscape:
+ return "landscape"
+
+ case .portrait:
+ return "portrait"
+ }
+ }
+ }
+
+ /// An enumeration of potential width conditions.
+ public enum SizeCondition {
+
+ /// Specifies the maximum width.
+ case maxWidth(String)
+
+ /// Specifies the target width.
+ case width(String)
+
+ /// Specifies the minimum width.
+ case minWidth(String)
+
+ /// Specifies a interface orientation.
+ case orientation(InterfaceOrientation)
+
+ /// The raw representation of the type.
+ internal var rawValue: String {
+
+ switch self {
+ case .maxWidth(let width):
+ return "(max-width: \(width))"
+
+ case .width(let width):
+ return "(width: \(width))"
+
+ case .minWidth(let width):
+ return "(min-width: \(width))"
+
+ case .orientation(let orientation):
+ return "(orientation: \(orientation.rawValue))"
+ }
+ }
+ }
+
+ /// The size of the candidate.
+ internal let size: String
+
+ /// The potential conditions of the candidate.
+ internal let conditions: [SizeCondition]?
+
+ /// The raw representation of the type.
+ internal var rawValue: String {
+
+ if let conditions = self.conditions {
+ return "\(conditions.map { $0.rawValue }.joined(separator: " and ")) \(size)"
+ }
+
+ return size
+ }
+
+ /// Create a size candidate.
+ ///
+ /// - Parameters:
+ /// - size: The width to apply.
+ /// - conditions: The conditions under which the size should be applied.
+ public init(_ size: String, conditions: [SizeCondition]? = nil) {
+
+ self.size = size
+ self.conditions = conditions
+ }
+
+ /// Create a size candidate.
+ ///
+ /// - Parameters:
+ /// - size: The width to apply.
+ /// - conditions: The conditions under which the size should be applied.
+ public init(_ size: String, conditions: SizeCondition...) {
+
+ self.size = size
+ self.conditions = conditions
+ }
+}
diff --git a/Sources/HTMLKit/Abstraction/Types/SourceCandidate.swift b/Sources/HTMLKit/Abstraction/Types/SourceCandidate.swift
new file mode 100644
index 00000000..6a529959
--- /dev/null
+++ b/Sources/HTMLKit/Abstraction/Types/SourceCandidate.swift
@@ -0,0 +1,104 @@
+import Foundation
+
+/// A type that represents a source candidate.
+///
+/// The candidate is used to define an alternative source with an additional condition.
+///
+/// ```swift
+/// Image()
+/// .source("...png")
+/// .sourceSet(
+/// SourceCandidate("...png", width: 1024),
+/// SourceCandiate("...png", width: 1680)
+/// )
+/// ```
+public struct SourceCandidate {
+
+ /// An enumeration of potential pixel densities.
+ public enum PixelDensity {
+
+ /// Specifies a 1:1 density.
+ case regular
+
+ /// Specifies a 2:1 density.
+ case high
+
+ /// Specifies a 3:1 density.
+ case ultra
+
+ /// The raw representation of the type.
+ internal var rawValue: String {
+
+ switch self {
+ case .regular:
+ return "1x"
+
+ case .high:
+ return "2x"
+
+ case .ultra:
+ return "3x"
+ }
+ }
+ }
+
+ /// The source path of the candidate.
+ internal let source: String
+
+ /// The potential condition of the candidate.
+ internal let condition: String?
+
+ /// The raw representation of the type.
+ internal var rawValue: String {
+
+ if let condition = self.condition {
+ return "\(source) \(condition)"
+ }
+
+ return source
+ }
+
+ /// Create a source candidate.
+ ///
+ /// - Parameters:
+ /// - source: The url path to load from.
+ /// - width: The width to apply.
+ public init(_ source: String) {
+
+ self.source = source
+ self.condition = nil
+ }
+
+ /// Create a source candidate.
+ ///
+ /// - Parameters:
+ /// - source: The url path to load from.
+ /// - width: The width to apply.
+ public init(_ source: String, width: Int) {
+
+ self.source = source
+ self.condition = "\(width)w"
+ }
+
+ /// Create a source candidate.
+ ///
+ /// - Parameters:
+ /// - source: The url path to load from.
+ /// - density: The density to apply.
+ public init(_ source: String, density: Int) {
+
+ self.source = source
+ self.condition = "\(density)x"
+ }
+
+ /// Create a source candidate.
+ ///
+ /// - Parameters:
+ /// - source: The url path to load from.
+ /// - density: The density to apply.
+ public init(_ source: String, density: PixelDensity) {
+
+ self.source = source
+ self.condition = density.rawValue
+ }
+}
diff --git a/Sources/HTMLKit/Abstraction/Types/UnitPoint.swift b/Sources/HTMLKit/Abstraction/Types/UnitPoint.swift
new file mode 100644
index 00000000..a62e5ccc
--- /dev/null
+++ b/Sources/HTMLKit/Abstraction/Types/UnitPoint.swift
@@ -0,0 +1,77 @@
+/// A type represents a unit point.
+///
+/// The point is used to define a position by two coordinates.
+///
+/// ```swift
+/// Vector {
+/// Circle {
+/// }
+/// .center(UnitPoint(x: 50, y: 50))
+/// }
+/// ```
+public struct UnitPoint {
+
+ /// An enumeration of potential point formats.
+ public enum PointFormat {
+
+ /// Indicates an absolute value.
+ case absolute
+
+ /// Indicates an relative value.
+ case relative
+
+ /// Returns the string representation based on the format.
+ func string(from value: Int) -> String {
+
+ switch self {
+ case .absolute:
+ return "\(value)"
+
+ case .relative:
+ return "\(value)%"
+ }
+ }
+
+ /// Returns the string representation based on the format.
+ func string(from value: Double) -> String {
+
+ switch self {
+ case .absolute:
+ return "\(value)"
+
+ case .relative:
+ return "\(value)%"
+ }
+ }
+ }
+
+ /// The horizontal coordinate of the point.
+ internal var x: String
+
+ /// The vertical coordinate 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) {
+
+ self.x = format.string(from: x)
+ self.y = format.string(from: 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) {
+
+ self.x = format.string(from: x)
+ self.y = format.string(from: y)
+ }
+}
diff --git a/Tests/HTMLKitTests/AttributesTests.swift b/Tests/HTMLKitTests/AttributesTests.swift
index f671e29b..2036c5a5 100644
--- a/Tests/HTMLKitTests/AttributesTests.swift
+++ b/Tests/HTMLKitTests/AttributesTests.swift
@@ -160,8 +160,20 @@ final class AttributesTests: XCTestCase {
return self.mutate(translate: value.rawValue)
}
- func accept(_ value: String) -> Tag {
- return self.mutate(accept: value)
+ func accept(_ specifiers: [String]) -> Tag {
+ return self.mutate(accept: specifiers.joined(separator: ", "))
+ }
+
+ func accept(_ specifiers: String...) -> Tag {
+ return self.mutate(accept: specifiers.joined(separator: ", "))
+ }
+
+ func accept(_ specifiers: [Values.Media]) -> Tag {
+ return self.mutate(accept: specifiers.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ func accept(_ specifiers: Values.Media...) -> Tag {
+ return self.mutate(accept: specifiers.map { $0.rawValue }.joined(separator: ", "))
}
func action(_ value: String) -> Tag {
@@ -357,6 +369,14 @@ final class AttributesTests: XCTestCase {
return self.mutate(media: value)
}
+ func media(_ queries: [MediaQuery]) -> Tag {
+ return self.mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ func media(_ queries: MediaQuery...) -> Tag {
+ return self.mutate(media: queries.map { $0.rawValue }.joined(separator: ", "))
+ }
+
func method(_ value: HTMLKit.Values.Method) -> Tag {
return self.mutate(method: value.rawValue)
}
@@ -497,8 +517,12 @@ final class AttributesTests: XCTestCase {
return self.mutate(size: size)
}
- func sizes(_ size: Int) -> Tag {
- return self.mutate(sizes: size)
+ func sizes(_ candidates: [SizeCandidate]) -> Tag {
+ return self.mutate(sizes: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ func sizes(_ candidates: SizeCandidate...) -> Tag {
+ return self.mutate(sizes: candidates.map { $0.rawValue }.joined(separator: ", "))
}
func slot(_ value: String) -> Tag {
@@ -525,8 +549,12 @@ final class AttributesTests: XCTestCase {
return mutate(sourcelanguage: value.rawValue)
}
- func sourceSet(_ value: String) -> Tag {
- return mutate(sourceset: value)
+ func sourceSet(_ candidates: [SourceCandidate]) -> Tag {
+ return mutate(sourceset: candidates.map { $0.rawValue }.joined(separator: ", "))
+ }
+
+ func sourceSet(_ candidates: SourceCandidate...) -> Tag {
+ return mutate(sourceset: candidates.map { $0.rawValue }.joined(separator: ", "))
}
func start(_ size: Int) -> Tag {
@@ -616,21 +644,65 @@ final class AttributesTests: XCTestCase {
}
func positionPoint(_ point: (Int, Int)) -> Tag {
- return self.mutate(positionpoint: point)
+ return self.mutate(x: "\(point.0)").mutate(y: "\(point.1)")
+ }
+
+ 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)
+ return self.mutate(rx: "\(point.0)").mutate(ry: "\(point.1)")
+ }
+
+ 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)
+ return self.mutate(cx: "\(point.0)").mutate(cy: "\(point.1)")
+ }
+
+ 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)
}
@@ -1075,12 +1147,20 @@ final class AttributesTests: XCTestCase {
func testAcceptAttribute() throws {
let view = TestView {
- Tag {}.accept("accept")
+ Tag {}.accept("image/*")
+ Tag {}.accept([".jpg", ".png", ".svg"])
+ Tag {}.accept(".jpg", ".png", ".svg")
+ Tag {}.accept([.ogg, .mpeg])
+ Tag {}.accept(.ogg, .mpeg)
}
XCTAssertEqual(try renderer.render(view: view),
"""
-
+ \
+ \
+ \
+ \
+
"""
)
}
@@ -1627,12 +1707,16 @@ final class AttributesTests: XCTestCase {
func testMediaAttribute() throws {
let view = TestView {
- Tag {}.media("print and (resolution:300dpi)")
+ Tag {}.media(MediaQuery(.all, features: .orientation(.landscape), .resolution("300dpi")))
+ Tag {}.media(MediaQuery(.all), MediaQuery(.print))
+ Tag {}.media(MediaQuery(.all), MediaQuery(.print, features: [.maxHeight("20vh")]))
}
XCTAssertEqual(try renderer.render(view: view),
"""
-
+ \
+ \
+
"""
)
}
@@ -2023,12 +2107,26 @@ final class AttributesTests: XCTestCase {
func testSizesAttribute() throws {
let view = TestView {
- Tag {}.sizes(2)
+ Tag {}.sizes(SizeCandidate("auto"))
+ Tag {}.sizes(SizeCandidate("100vw", conditions: .orientation(.landscape)))
+ Tag {}.sizes(SizeCandidate("100vw", conditions: .orientation(.portrait)))
+ Tag {}.sizes(SizeCandidate("100vw", conditions: .orientation(.landscape), .width("50em")))
+ Tag {}.sizes(SizeCandidate("calc(100vw - 100px)", conditions: .minWidth("50em")))
+ Tag {}.sizes(SizeCandidate("100vw", conditions: .maxWidth("50em")))
+ Tag {}.sizes([SizeCandidate("100vw"), SizeCandidate("100vw", conditions: .maxWidth("50em"))])
+ Tag {}.sizes(SizeCandidate("100vw"), SizeCandidate("100vw", conditions: .maxWidth("50em")))
}
XCTAssertEqual(try renderer.render(view: view),
"""
-
+ \
+ \
+ \
+ \
+ \
+ \
+ \
+
"""
)
}
@@ -2101,12 +2199,20 @@ final class AttributesTests: XCTestCase {
func testSourceSetAttribute() throws {
let view = TestView {
- Tag {}.sourceSet("img2.png 100w, img3.png 500w")
+ Tag {}.sourceSet(SourceCandidate("img.webp"))
+ Tag {}.sourceSet(SourceCandidate("img.png", density: 4))
+ Tag {}.sourceSet(SourceCandidate("img.png", density: .ultra))
+ Tag {}.sourceSet(SourceCandidate("img.png", width: 1024))
+ Tag {}.sourceSet(SourceCandidate("img.png", width: 1024), SourceCandidate("img.png", density: .ultra))
}
XCTAssertEqual(try renderer.render(view: view),
"""
-
+ \
+ \
+ \
+ \
+
"""
)
}
@@ -2891,15 +2997,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 +3019,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 +3038,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 +3057,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),
"""
-
+ \
+
"""
)
}