diff --git a/Sources/HTMLKit/External/Attributes/VectorAttributes.swift b/Sources/HTMLKit/External/Attributes/VectorAttributes.swift new file mode 100644 index 00000000..ee28a01e --- /dev/null +++ b/Sources/HTMLKit/External/Attributes/VectorAttributes.swift @@ -0,0 +1,398 @@ +/// ## Description +/// The file contains the basic attribute handlers. +/// +/// ## Note +/// If you about to add something to the file, stick to the official documentation to keep the code consistent. +/// +/// ## Authors +/// Mats Moll: https://github.com/matsmoll +/// Mattes Mohr: https://github.com/mattesmohr + +import OrderedCollections + +/// ## Description +/// The alias combines the global attributes. +/// +/// ## References +public typealias GlobalVectorAttributes = IdentifierAttribute & TabulatorAttribute & ClassAttribute & StyleAttribute & FillAttribute & FillOpacityAttribute & StrokeAttribute & StrokeWidthAttribute & StrokeOpacityAttribute & StrokeLineCapAttribute & StrokeLineJoinAttribute + +/// ## Description +/// The protocol provides the element with the fill handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol FillAttribute: AnyAttribute { + + /// The func adds + /// + /// + func fill(_ value: String) -> Self +} + +extension FillAttribute { + + internal var key: String { "fill" } +} + +extension FillAttribute where Self: ContentNode { + + internal func mutate(fill value: String) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the fill-opacity handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol FillOpacityAttribute: AnyAttribute { + + /// The func adds + /// + /// + func fillOpacity(_ value: Double) -> Self +} + +extension FillOpacityAttribute { + + internal var key: String { "fill-opacity" } +} + +extension FillOpacityAttribute where Self: ContentNode { + + internal func mutate(fillopacity value: Double) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the stroke handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol StrokeAttribute: AnyAttribute { + + /// The func adds + /// + /// + func stroke(_ value: String) -> Self +} + +extension StrokeAttribute { + + internal var key: String { "stroke" } +} + +extension StrokeAttribute where Self: ContentNode { + + internal func mutate(stroke value: String) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the stroke-width handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol StrokeWidthAttribute: AnyAttribute { + + /// The func adds + /// + /// + func strokeWidth(_ size: Int) -> Self +} + +extension StrokeWidthAttribute { + + internal var key: String { "stroke-width" } +} + +extension StrokeWidthAttribute where Self: ContentNode { + + internal func mutate(strokewidth value: Int) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the stroke-opacity handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol StrokeOpacityAttribute: AnyAttribute { + + /// The func adds + /// + /// + func strokeOpacity(_ value: Double) -> Self +} + +extension StrokeOpacityAttribute { + + internal var key: String { "stroke-opacity" } +} + +extension StrokeOpacityAttribute where Self: ContentNode { + + internal func mutate(strokeopacity value: Double) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the stroke-linecap handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol StrokeLineCapAttribute: AnyAttribute { + + /// The func adds + /// + /// + func strokeLineCap(_ type: Linecap) -> Self +} + +extension StrokeLineCapAttribute { + + internal var key: String { "stroke-linecap" } +} + +extension StrokeLineCapAttribute where Self: ContentNode { + + internal func mutate(strokelinecap value: String) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the stroke-linejoin handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol StrokeLineJoinAttribute: AnyAttribute { + + /// The func adds + /// + /// + func strokeLineJoin(_ type: Linejoin) -> Self +} + +extension StrokeLineJoinAttribute { + + internal var key: String { "stroke-linejoin" } +} + +extension StrokeLineJoinAttribute where Self: ContentNode { + + internal func mutate(strokelinejoin value: String) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the radius handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol RadiusAttribute: AnyAttribute { + + /// The func adds + /// + /// + func radius(_ size: Int) -> Self +} + +extension RadiusAttribute { + + internal var key: String { "r" } +} + +extension RadiusAttribute where Self: ContentNode { + + internal func mutate(radius value: Int) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} + +/// ## Description +/// The protocol provides the element with the radius handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol PositionPointAttribute: AnyAttribute { + + /// The func adds + /// + /// + func positionPoint(_ point: Point) -> Self +} + +extension PositionPointAttribute where Self: ContentNode { + + internal func mutate(radius: Point) -> Self { + + guard var attributes = self.attributes else { + + var attributes = OrderedDictionary() + attributes["x"] = radius.x + attributes["y"] = radius.y + + return .init(attributes: attributes, content: content) + } + + attributes["x"] = radius.x + attributes["y"] = radius.y + + return .init(attributes: attributes, content: content) + } +} + +/// ## Description +/// The protocol provides the element with the radius handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol RadiusPointAttribute: AnyAttribute { + + /// The func adds + /// + /// + func radiusPoint(_ point: Point) -> Self +} + +extension RadiusPointAttribute where Self: ContentNode { + + internal func mutate(radius: Point) -> Self { + + guard var attributes = self.attributes else { + + var attributes = OrderedDictionary() + attributes["rx"] = radius.x + attributes["ry"] = radius.y + + return .init(attributes: attributes, content: content) + } + + attributes["rx"] = radius.x + attributes["ry"] = radius.y + + return .init(attributes: attributes, content: content) + } +} + +/// ## Description +/// The protocol provides the element with the radius handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol CenterPointAttribute: AnyAttribute { + + /// The func adds + /// + /// + func centerPoint(_ point: Point) -> Self +} + +extension CenterPointAttribute where Self: ContentNode { + + internal func mutate(centerpoint: Point) -> Self { + + guard var attributes = self.attributes else { + + var attributes = OrderedDictionary() + attributes["cx"] = centerpoint.x + attributes["cy"] = centerpoint.y + + return .init(attributes: attributes, content: content) + } + + attributes["cx"] = centerpoint.x + attributes["cy"] = centerpoint.y + + return .init(attributes: attributes, content: content) + } +} + +/// ## Description +/// The protocol provides the element with the viewbox handler. +/// +/// ## References +/// https://html.spec.whatwg.org/#attr-option-selected +/// +public protocol ViewBoxAttribute: AnyAttribute { + + /// The func adds + /// + /// + func viewBox(_ value: String) -> Self +} + +extension ViewBoxAttribute { + + internal var key: String { "viewbox" } +} + +extension ViewBoxAttribute where Self: ContentNode { + + internal func mutate(viewbox value: String) -> Self { + + guard var attributes = self.attributes else { + return .init(attributes: set(key: key, value: value), content: content) + } + + return .init(attributes: update(key: key, value: value, on: &attributes), content: content) + } +} diff --git a/Sources/HTMLKit/External/Elements/BodyElements.swift b/Sources/HTMLKit/External/Elements/BodyElements.swift index 4a8861a3..ee8a84fe 100644 --- a/Sources/HTMLKit/External/Elements/BodyElements.swift +++ b/Sources/HTMLKit/External/Elements/BodyElements.swift @@ -191,6 +191,11 @@ public typealias Iframe = InlineFrame /// public typealias Param = Parameter +/// ## Description +/// The alias points to Parameter. +/// +public typealias Svg = Vector + /// ## Description /// The element represents a self-contained content. /// @@ -14357,3 +14362,101 @@ extension Table: Modifiable { } } } + +/// ## Description +/// The element represents a vector. +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Vector: ContentNode, HtmlElement, BodyElement, FormElement, FigureElement, ObjectElement { + + internal var name: String { "svg" } + + internal var attributes: OrderedDictionary? + + internal var content: [VectorElement] + + public init(@ContentBuilder content: () -> [VectorElement]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [VectorElement]) { + self.attributes = attributes + self.content = content + } +} + +extension Vector: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + return try self.build(with: manager) + } +} + +extension Vector: GlobalVectorAttributes, WidthAttribute, HeightAttribute, ViewBoxAttribute { + + public func id(_ value: String) -> Vector { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Vector { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Vector { + return self.mutate(tabindex: value) + } + + public func width(_ size: Int) -> Vector { + return self.mutate(width: size) + } + + public func height(_ size: Int) -> Vector { + return self.mutate(height: size) + } + + public func `class`(_ value: String) -> Vector { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Vector { + return self.mutate(style: value) + } + + public func viewBox(_ value: String) -> Vector { + return self.mutate(viewbox: value) + } + + public func fill(_ value: String) -> Vector { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Vector { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Vector { + return self.mutate(strokewidth: size) + } + + public func fillOpacity(_ value: Double) -> Vector { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Vector { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Vector { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Vector { + return self.mutate(strokelinejoin: type.rawValue) + } +} diff --git a/Sources/HTMLKit/External/Elements/VectorElements.swift b/Sources/HTMLKit/External/Elements/VectorElements.swift new file mode 100644 index 00000000..a72a8e0b --- /dev/null +++ b/Sources/HTMLKit/External/Elements/VectorElements.swift @@ -0,0 +1,738 @@ +import OrderedCollections + +/// ## Description +/// The alias points to Rectangle. +/// +public typealias Rect = Rectangle + +/// ## Description +/// The element represents ... +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Circle: ContentNode, VectorElement { + + internal var name: String { "circle" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Circle: GlobalVectorAttributes, CenterPointAttribute, RadiusAttribute { + + public func id(_ value: String) -> Circle { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Circle { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Circle { + return self.mutate(tabindex: value) + } + + public func `class`(_ value: String) -> Circle { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Circle { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Circle { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Circle { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Circle { + return self.mutate(strokewidth: size) + } + + public func centerPoint(_ point: Point) -> Circle { + return self.mutate(centerpoint: point) + } + + public func radius(_ size: Int) -> Circle { + return self.mutate(radius: size) + } + + public func fillOpacity(_ value: Double) -> Circle { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Circle { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Circle { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Circle { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Circle: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} + +/// ## Description +/// The element represents ... +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Rectangle: ContentNode, VectorElement { + + internal var name: String { "rect" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Rectangle: GlobalVectorAttributes, WidthAttribute, HeightAttribute, RadiusPointAttribute { + + public func id(_ value: String) -> Rectangle { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Rectangle { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Rectangle { + return self.mutate(tabindex: value) + } + + public func `class`(_ value: String) -> Rectangle { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Rectangle { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Rectangle { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Rectangle { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Rectangle { + return self.mutate(strokewidth: size) + } + + public func radiusPoint(_ point: Point) -> Rectangle { + return self.mutate(radius: point) + } + + public func width(_ size: Int) -> Rectangle { + return self.mutate(width: size) + } + + public func height(_ size: Int) -> Rectangle { + return self.mutate(height: size) + } + + public func fillOpacity(_ value: Double) -> Rectangle { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Rectangle { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Rectangle { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Rectangle { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Rectangle: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} + +/// ## Description +/// The element represents a comment output. +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Ellipse: ContentNode, VectorElement { + + internal var name: String { "ellipse" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Ellipse: GlobalVectorAttributes, CenterPointAttribute, RadiusPointAttribute { + + public func id(_ value: String) -> Ellipse { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Ellipse { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Ellipse { + return self.mutate(tabindex: value) + } + + public func `class`(_ value: String) -> Ellipse { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Ellipse { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Ellipse { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Ellipse { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Ellipse { + return self.mutate(strokewidth: size) + } + + public func centerPoint(_ point: Point) -> Ellipse { + return self.mutate(centerpoint: point) + } + + public func radiusPoint(_ point: Point) -> Ellipse { + return self.mutate(radius: point) + } + + public func fillOpacity(_ value: Double) -> Ellipse { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Ellipse { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Ellipse { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Ellipse { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Ellipse: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} + +/// ## Description +/// The element represents ... +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Line: ContentNode, VectorElement { + + internal var name: String { "line" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Line: GlobalVectorAttributes { + + public func id(_ value: String) -> Line { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Line { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Line { + return self.mutate(tabindex: value) + } + + public func `class`(_ value: String) -> Line { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Line { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Line { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Line { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Line { + return self.mutate(strokewidth: size) + } + + public func fillOpacity(_ value: Double) -> Line { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Line { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Line { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Line { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Line: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} + +/// ## Description +/// The element represents a comment output. +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Polygon: ContentNode, VectorElement { + + internal var name: String { "polygon" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Polygon: GlobalVectorAttributes { + + public func id(_ value: String) -> Polygon { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Polygon { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Polygon { + return self.mutate(tabindex: value) + } + + public func `class`(_ value: String) -> Polygon { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Polygon { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Polygon { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Polygon { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Polygon { + return self.mutate(strokewidth: size) + } + + public func fillOpacity(_ value: Double) -> Polygon { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Polygon { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Polygon { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Polygon { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Polygon: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} + +/// ## Description +/// The element represents ... +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Polyline: ContentNode, VectorElement { + + internal var name: String { "polyline" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Polyline: GlobalVectorAttributes { + + public func id(_ value: String) -> Polyline { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Polyline { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Polyline { + return self.mutate(tabindex: value) + } + + public func `class`(_ value: String) -> Polyline { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Polyline { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Polyline { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Polyline { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Polyline { + return self.mutate(strokewidth: size) + } + + public func fillOpacity(_ value: Double) -> Polyline { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Polyline { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Polyline { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Polyline { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Polyline: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} + +/// ## Description +/// The element represents ... +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Path: ContentNode, VectorElement { + + internal var name: String { "path" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Path: GlobalVectorAttributes { + + public func id(_ value: String) -> Path { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Path { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Path { + return self.mutate(tabindex: value) + } + + public func `class`(_ value: String) -> Path { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Path { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Path { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Path { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Path { + return self.mutate(strokewidth: size) + } + + public func fillOpacity(_ value: Double) -> Path { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Path { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Path { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Path { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Path: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} + +/// ## Description +/// The element represents ... +/// +/// ## References +/// https://html.spec.whatwg.org/#the-table-element +/// +public struct Use: ContentNode, VectorElement { + + internal var name: String { "use" } + + internal var attributes: OrderedDictionary? + + internal var content: [AnyContent] + + public init(@ContentBuilder content: () -> [AnyContent]) { + self.content = content() + } + + internal init(attributes: OrderedDictionary?, content: [AnyContent]) { + self.attributes = attributes + self.content = content + } +} + +extension Use: GlobalVectorAttributes, ReferenceAttribute, WidthAttribute, HeightAttribute { + + public func id(_ value: String) -> Use { + return self.mutate(id: value) + } + + public func id(_ value: TemplateValue) -> Use { + return self.mutate(id: value.rawValue) + } + + public func tabIndex(_ value: String) -> Use { + return self.mutate(tabindex: value) + } + + public func reference(_ value: String) -> Use { + return self.mutate(href: value) + } + + public func reference(_ value: TemplateValue) -> Use { + return self.mutate(href: value.rawValue) + } + + public func width(_ size: Int) -> Use { + return self.mutate(width: size) + } + + public func height(_ size: Int) -> Use { + return self.mutate(height: size) + } + + public func `class`(_ value: String) -> Use { + return self.mutate(class: value) + } + + public func style(_ value: String) -> Use { + return self.mutate(style: value) + } + + public func fill(_ value: String) -> Use { + return self.mutate(fill: value) + } + + public func stroke(_ value: String) -> Use { + return self.mutate(stroke: value) + } + + public func strokeWidth(_ size: Int) -> Use { + return self.mutate(strokewidth: size) + } + + public func fillOpacity(_ value: Double) -> Use { + return self.mutate(fillopacity: value) + } + + public func strokeOpacity(_ value: Double) -> Use { + return self.mutate(strokeopacity: value) + } + + public func strokeLineCap(_ type: Linecap) -> Use { + return self.mutate(strokelinecap: type.rawValue) + } + + public func strokeLineJoin(_ type: Linejoin) -> Use { + return self.mutate(strokelinejoin: type.rawValue) + } +} + +extension Use: AnyContent { + + public func prerender(_ formula: Renderer.Formula) throws { + try self.build(formula) + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + try self.build(with: manager) + } +} diff --git a/Sources/HTMLKit/External/Sizes.swift b/Sources/HTMLKit/External/Sizes.swift new file mode 100644 index 00000000..8463f834 --- /dev/null +++ b/Sources/HTMLKit/External/Sizes.swift @@ -0,0 +1,5 @@ +public struct Point { + + var x: Int + var y: Int +} diff --git a/Sources/HTMLKit/External/Types.swift b/Sources/HTMLKit/External/Types.swift index c1260a6e..5cda6584 100644 --- a/Sources/HTMLKit/External/Types.swift +++ b/Sources/HTMLKit/External/Types.swift @@ -580,3 +580,27 @@ public enum Roles: String { case widget case window } + +/// ## Description +/// The type is for +/// +/// ## References +/// +public enum Linecap: String { + + case butt + case square + case round +} + +/// ## Description +/// The type is for +/// +/// ## References +/// +public enum Linejoin: String { + + case miter + case round + case bevel +} diff --git a/Sources/HTMLKit/Internal/Core/Elements.swift b/Sources/HTMLKit/Internal/Core/Elements.swift index 647eeaa6..24214263 100644 --- a/Sources/HTMLKit/Internal/Core/Elements.swift +++ b/Sources/HTMLKit/Internal/Core/Elements.swift @@ -121,3 +121,11 @@ public protocol TableElement: AnyElement { /// public protocol HtmlElement: AnyElement { } + +/// ## Description +/// The protocol defines a vector element. +/// +/// ## References +/// +public protocol VectorElement: AnyElement { +} diff --git a/Sources/HTMLKit/Internal/Core/Extensions/Datatypes+Content.swift b/Sources/HTMLKit/Internal/Core/Extensions/Datatypes+Content.swift index 1c9770b4..bb223b9b 100644 --- a/Sources/HTMLKit/Internal/Core/Extensions/Datatypes+Content.swift +++ b/Sources/HTMLKit/Internal/Core/Extensions/Datatypes+Content.swift @@ -243,6 +243,21 @@ extension Array where Element == HtmlElement { } } +extension Array where Element == VectorElement { + + public func prerender(_ formula: Renderer.Formula) throws { + try forEach { try $0.prerender(formula) } + } + + public func render(with manager: Renderer.ContextManager) throws -> String { + return try self.reduce("") { try $0 + $1.render(with: manager) } + } + + public var scripts: AnyContent { + return self.reduce("") { $0 + $1.scripts } + } +} + /// The extension is /// /// diff --git a/Sources/HTMLKit/Internal/Core/Nodes.swift b/Sources/HTMLKit/Internal/Core/Nodes.swift index 4a89e8be..40aefc12 100644 --- a/Sources/HTMLKit/Internal/Core/Nodes.swift +++ b/Sources/HTMLKit/Internal/Core/Nodes.swift @@ -501,6 +501,36 @@ extension ContentNode where Content == HtmlElement { } } +extension ContentNode where Content == VectorElement { + + internal func build(_ formula: Renderer.Formula) throws { + + formula.add(string: "<\(name)") + + if let attributes = attributes { + + attributes.forEach { attribute in + formula.add(string: " \(attribute.key)=\"\(attribute.value)\"") + } + } + + formula.add(string: ">") + + try content.prerender(formula) + + formula.add(string: "") + } + + internal func build(with manager: Renderer.ContextManager) throws -> String { + + guard let attributes = attributes else { + return try "<\(name)>\(content.render(with: manager))" + } + + return try "<\(name)" + attributes.map { attribute in return " \(attribute.key)=\"\(attribute.value)\"" } + ">\(content.render(with: manager))" as! String + } +} + extension ContentNode where Content == String { internal func build(_ formula: Renderer.Formula) throws { diff --git a/Tests/HTMLKitTests/ElementTests.swift b/Tests/HTMLKitTests/ElementTests.swift index f53d0b2d..2a928c37 100644 --- a/Tests/HTMLKitTests/ElementTests.swift +++ b/Tests/HTMLKitTests/ElementTests.swift @@ -1740,6 +1740,150 @@ final class ElementTests: XCTestCase { """ ) } + + func testVectorElement() throws { + + let view = TestPage { + Vector { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testCircleElement() throws { + + let view = TestPage { + Circle { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testRectangleElement() throws { + + let view = TestPage { + Rectangle { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testEllipseElement() throws { + + let view = TestPage { + Ellipse { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testLineElement() throws { + + let view = TestPage { + Line { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testPolygonElement() throws { + + let view = TestPage { + Polygon { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testPolylineElement() throws { + + let view = TestPage { + Polyline { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testPathElement() throws { + + let view = TestPage { + Path { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } + + func testUseElement() throws { + + let view = TestPage { + Use { + } + } + + try renderer.add(view: view) + + XCTAssertEqual(try renderer.render(raw: TestPage.self), + """ + + """ + ) + } } extension ElementTests { @@ -1852,6 +1996,15 @@ extension ElementTests { ("testScriptElement", testScriptElement), ("testNoScriptElement", testNoScriptElement), ("testTemplateElement", testTemplateElement), - ("testCanvasElement", testCanvasElement) + ("testCanvasElement", testCanvasElement), + ("testVectorElement", testVectorElement), + ("testCircleElement", testCircleElement), + ("testRectangleElement", testRectangleElement), + ("testEllipseElement", testEllipseElement), + ("testLineElement", testLineElement), + ("testPolygonElement", testPolygonElement), + ("testPolylineElement", testPolylineElement), + ("testPathElement", testPathElement), + ("testUseElement", testUseElement) ] }