From 17432d216827a0f9dd098df80dd64e6375466647 Mon Sep 17 00:00:00 2001
From: Stephann V <3025661+stephannv@users.noreply.github.com>
Date: Fri, 21 Apr 2023 09:54:35 -0300
Subject: [PATCH] feat: Support SVG rendering (#42)
---
spec/blueprint/html/svg_spec.cr | 29 +++++++++++++++++++
spec/blueprint/svg/component_spec.cr | 42 ++++++++++++++++++++++++++++
src/blueprint/html.cr | 3 +-
src/blueprint/html/svg.cr | 13 +++++++++
src/blueprint/svg/component.cr | 24 ++++++++++++++++
5 files changed, 110 insertions(+), 1 deletion(-)
create mode 100644 spec/blueprint/html/svg_spec.cr
create mode 100644 spec/blueprint/svg/component_spec.cr
create mode 100644 src/blueprint/html/svg.cr
create mode 100644 src/blueprint/svg/component.cr
diff --git a/spec/blueprint/html/svg_spec.cr b/spec/blueprint/html/svg_spec.cr
new file mode 100644
index 0000000..705ebe2
--- /dev/null
+++ b/spec/blueprint/html/svg_spec.cr
@@ -0,0 +1,29 @@
+require "../../spec_helper"
+
+private class Example
+ include Blueprint::HTML
+
+ private def blueprint
+ svg width: 30, height: 10 do
+ g fill: :red do
+ rect x: 0, y: 0, width: 10, height: 10
+ rect x: 20, y: 0, width: 10, height: 10
+ end
+ end
+ end
+end
+
+describe Blueprint::HTML do
+ it "allows SVG rendering" do
+ example = Example.new
+
+ example.to_html.should eq <<-HTML.strip.gsub(/\R\s+/, "")
+
+ HTML
+ end
+end
diff --git a/spec/blueprint/svg/component_spec.cr b/spec/blueprint/svg/component_spec.cr
new file mode 100644
index 0000000..660c24b
--- /dev/null
+++ b/spec/blueprint/svg/component_spec.cr
@@ -0,0 +1,42 @@
+require "../../spec_helper"
+
+private ELEMENTS = %w(a animate animateMotion animateTransform circle clipPath defs desc discard ellipse feBlend
+ feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight
+ feDropShadow feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset
+ fePointLight feSpecularLighting feSpotLight feTile feTurbulence filter foreignObject g image line linearGradient
+ marker mask metadata mpath path pattern polygon polyline radialGradient rect script set stop style svg switch symbol
+ text textPath title tspan use view
+)
+
+private class DummyPage
+ include Blueprint::HTML
+
+ private def blueprint
+ svg do
+ {% for element in ELEMENTS %}
+ {{element.id}}
+ {{element.id}}(attribute: "test")
+ {{element.id}} { "content" }
+ {{element.id}}(attribute: "test") { "content" }
+ {% end %}
+ end
+ end
+end
+
+describe "Bluprint::SVG::Component" do
+ it "defines all SVG element helper methods" do
+ page = DummyPage.new
+ expected_html = String.build do |io|
+ io << ""
+ end
+
+ page.to_html.should eq expected_html
+ end
+end
diff --git a/src/blueprint/html.cr b/src/blueprint/html.cr
index edf615c..b15efb7 100644
--- a/src/blueprint/html.cr
+++ b/src/blueprint/html.cr
@@ -1,12 +1,13 @@
require "html"
require "./html/attributes_parser"
+require "./html/builder"
require "./html/content_capture"
require "./html/element_registrar"
require "./html/renderer"
require "./html/standard_elements"
+require "./html/svg"
require "./html/utils"
-require "./html/builder"
module Blueprint::HTML
@buffer = IO::Memory.new
diff --git a/src/blueprint/html/svg.cr b/src/blueprint/html/svg.cr
new file mode 100644
index 0000000..b4c905d
--- /dev/null
+++ b/src/blueprint/html/svg.cr
@@ -0,0 +1,13 @@
+require "../svg/component"
+
+module Blueprint::HTML
+ def svg(**attributes, &)
+ render Blueprint::SVG::Component.new(**attributes) do |component|
+ with component yield
+ end
+ end
+
+ def svg(**attributes)
+ svg(**attributes) { }
+ end
+end
diff --git a/src/blueprint/svg/component.cr b/src/blueprint/svg/component.cr
new file mode 100644
index 0000000..b559b2e
--- /dev/null
+++ b/src/blueprint/svg/component.cr
@@ -0,0 +1,24 @@
+struct Blueprint::SVG::Component(T)
+ include Blueprint::HTML
+
+ @attributes : T
+
+ def self.new(**kwargs)
+ new kwargs
+ end
+
+ {% for tag in %w(a animate animateMotion animateTransform circle clipPath defs desc discard ellipse feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feDropShadow feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence filter foreignObject g image line linearGradient marker mask metadata mpath path pattern polygon polyline radialGradient rect script set stop style svg switch symbol text textPath title tspan use view) %}
+ register_element {{tag}}
+ {% end %}
+
+ def initialize(attributes : T)
+ {% T.raise "Expected T be NamedTuple, but got #{T}." unless T <= NamedTuple %}
+ @attributes = attributes
+ end
+
+ def blueprint(&)
+ element :svg, **@attributes do
+ yield
+ end
+ end
+end