vecty icon indicating copy to clipboard operation
vecty copied to clipboard

Inline SVG

Open dave opened this issue 7 years ago • 16 comments

I'm having trouble getting inline SVG working:

vecty.Tag(
	"svg",
	vecty.Markup(
		vecty.Namespace("http://www.w3.org/2000/svg"),
		vecty.Attribute("width", "14"),
		vecty.Attribute("height", "16"),
		vecty.Attribute("viewBox", "0 0 14 16"),
	),
	vecty.Tag(
		"path",
		vecty.Markup(
			vecty.Attribute("fill-rule", "evenodd"),
			vecty.Attribute("d", "M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"),
		),
	),
),

... this doesn't render anything... Am I doing something wrong?

dave avatar Apr 02 '18 18:04 dave

Looks correct at first glance.

Is the element getting created in the DOM with the right attributes etc? i.e. have you looked at the Chrome inspector?

emidoots avatar Apr 02 '18 18:04 emidoots

Here's a demo: https://play.jsgo.io/1a1c614313ff086b371850be83c16b48c403d64a

dave avatar Apr 02 '18 18:04 dave

Should Namespace(...) be used for the xmlns attribute?

dave avatar Apr 02 '18 18:04 dave

screen shot 2018-04-02 at 20 25 32

dave avatar Apr 02 '18 18:04 dave

Adding the SVG in plain HTML works: https://jsfiddle.net/1L27s201/

dave avatar Apr 02 '18 18:04 dave

Hmm... this is what I see in the Chrome inspector with the "path" tag selected in the plain HTML version (the jsfiddle):

screen shot 2018-04-02 at 20 49 36

... but the attributes are missing in the right-hand panel in the vecty version:

screen shot 2018-04-02 at 20 49 56

dave avatar Apr 02 '18 18:04 dave

Should Namespace(...) be used for the xmlns attribute?

When vecty.Namespace is used, it results in vecty using document.createElementNS instead of document.createElement. See:

https://github.com/gopherjs/vecty/blob/19a11ec3acc3ae6c2e095b1ace2b7134c5f28c10/dom.go#L173-L176

image

Maybe it's worth trying to set it as a normal attribute?

image

dmitshur avatar Apr 02 '18 19:04 dmitshur

I tried what I mentioned above, no effect.

I found a fix though. It's to set vecty.Namespace in the path element as well:

vecty.Tag(
	"svg",
	vecty.Markup(
		vecty.Namespace("http://www.w3.org/2000/svg"),
		vecty.Attribute("width", "14"),
		vecty.Attribute("height", "16"),
		vecty.Attribute("viewBox", "0 0 14 16"),
	),
	vecty.Tag(
		"path",
		vecty.Markup(
			vecty.Namespace("http://www.w3.org/2000/svg"),
			vecty.Attribute("fill-rule", "evenodd"),
			vecty.Attribute("d", "M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"),
		),
	),
)

Try it at https://play.jsgo.io/e1bb676757854529b94715cf9f9a8766a7acc2b4.

I'm not sure if you're expected to do it manually, or if Vecty should be making the top-most Namespace apply recursively to all children.

dmitshur avatar Apr 02 '18 19:04 dmitshur

I'm not sure if you're expected to do it manually, or if Vecty should be making the top-most Namespace apply recursively to all children.

Currently any tags that require an alternative namespace must have it specified explicitly. We could possibly propagate it down, though that means the only way to change namespace again is to set it explicitly, and we'd probably need to do some special handling for Components, since they may be nested inside a namespaced element, but perform localised rendering.

This behaviour is only really meant as a stop-gap to make SVGs at least possible to work with, until we can generate an SVG package: #54

pdf avatar Apr 02 '18 21:04 pdf

This behaviour is only really meant as a stop-gap to make SVGs at least possible to work with, until we can generate an SVG package: #54

I actually don't think the generated SVG package will help here. After all, it still needs a simple API to create such elements as well. I see a few options:

  1. Implement the 'cascading' logic for namespaces -- this sounds complex and probably not worth it.
  2. Add something like vecty.SVGTag("path", ...) instead of requiring vecty.Namespace.
  3. Add a whitelist of element names (that do not conflict with HTML element names) that automatically get the svg namespace.

Not sure which of those would be the best. But writing your own SVGTag wrapper should be pretty easy today, regardless of what we decide to do here long term.

emidoots avatar Apr 03 '18 00:04 emidoots

I actually don't think the generated SVG package will help here. After all, it still needs a simple API to create such elements as well.

The current approach is only annoying because you have to repeat the namespace call manually - for generated code, this is no problem, right?

pdf avatar Apr 03 '18 00:04 pdf

Consider the generated elem.Anchor code:

func Anchor(markup ...vecty.MarkupOrChild) *vecty.HTML {
	return vecty.Tag("a", markup...)
}

If we needed to specify the vecty.Namespace there, too, we'd end up with TONS of duplication in the generated code.

func Anchor(markup ...vecty.MarkupOrChild) *vecty.HTML {
        return vecty.Tag("a", append(markup, vecty.Namespace("http://www.w3.org/2000/svg")...)
}

So instead we'd probably end up writing a helper function that does that repetitive logic, and I'm saying that may be useful here as well.

In general, I think we should think of the generated subpackages as "additional layers of type-safety", like how elem.Div prevents you from typo'ing "div" and not getting a compiler warning about it. But using the plain API without that type safety should also be doable, without a ton of hinderence, I think?

emidoots avatar Apr 03 '18 00:04 emidoots

Hrm, I guess we need to worry about code size for GopherJS. I have no problem with SVGTag() (my original PR for namespacing actually implemented it using NamespacedTag() :wink: ), if that's the way you want to head.

pdf avatar Apr 03 '18 07:04 pdf

Add a whitelist of element names (that do not conflict with HTML element names) that automatically get the svg namespace.

It looks like there's a few SVG elements that might conflict with HTML elements: https://developer.mozilla.org/en-US/docs/Web/SVG/Element

dave avatar Apr 03 '18 11:04 dave

It seems that when doing the vecty.Namespace() on nested svgs (like this example) it doesn't work well.

vecty.Tag(
	"svg",
	vecty.Markup(
		vecty.Attribute("xmlns", "http://www.w3.org/2000/svg"),
		vecty.Namespace("http://www.w3.org/2000/svg"),
		vecty.Attribute("viewbox", " 0 0 100 100"),
		vecty.Attribute("fill", "grey"),
		vecty.Attribute("stroke", "red"),
	),
	vecty.Tag(
		"circle", vecty.Markup(
			vecty.Namespace("http://www.w3.org/2000/svg"),
			vecty.Attribute("cx", "50"),
			vecty.Attribute("cy", "50"),
			vecty.Attribute("r", "40"),
		),
	),
	vecty.Tag(
		"svg",
		vecty.Markup(
			vecty.Namespace("http://www.w3.org/2000/svg"),
			vecty.Attribute("viewbox", " 0 0 10 10"),
			vecty.Attribute("x", "200"),
			vecty.Attribute("width", "100"),
		),
		vecty.Tag(
			"circle", vecty.Markup(
				vecty.Namespace("http://www.w3.org/2000/svg"),
				vecty.Attribute("cx", "5"),
				vecty.Attribute("cy", "5"),
				vecty.Attribute("r", "4"),
			),
		),
	),
),

Any thoughts on why a nested svg wouldn't work?

nathanhack avatar Apr 16 '20 12:04 nathanhack

For what it's worth I made a simple svg lib to work with vecty. The above can be written like this:

return elem.Div(
	svg.Render(
		svg.SVG(
			attr.Width(300),
			attr.Height(100),
			attr.ViewBox(0, 0, 300, 100),
			attr.Stroke("red"),
			attr.Fill("grey"),
			elm.Circle(attr.Cx(50), attr.Cy(50), attr.R(40)),
			elm.Circle(attr.Cx(150), attr.Cy(50), attr.R(4)),
		),
	),
)

nathanhack avatar Apr 18 '20 14:04 nathanhack