elm / svg Goto Github PK
View Code? Open in Web Editor NEWFast SVG in Elm
Home Page: https://package.elm-lang.org/packages/elm/svg/latest
License: BSD 3-Clause "New" or "Revised" License
Fast SVG in Elm
Home Page: https://package.elm-lang.org/packages/elm/svg/latest
License: BSD 3-Clause "New" or "Revised" License
This is a duplicate of issue #3 , where passing an Html.Attributes.class
instance to an svg
function results in a runtime error, even though the code compiles just fine.
Example:
svg [ class "fill-green-400" ] [ Icons.Outlined.check 20 Neutral ]
this compiles fine, but on runtime, we get an infinite loop of
Cannot set property className of #<SVGElement> which only has a getter
I know (through asking in Elm Slack - it took away about an hour of my time), that I need to pass in Svg.Attributes.class
, but this is still an important bug to fix in my opinion. Here are two reasons why:
Html.Attribute
and Svg
that I would get back an Html msg
without side effects. I gave it valid instances of both and saw a side effect (detailed above). This is slightly worse than public Html<M> svg(List<Html.Attribute> attributes, List<Svg> svgs) throws Exception
, because at least the method signature says the that it throws.I saw the counterargument that I should know better about the difference between SVGs and HTMLs to pass in the right class method, but I counter that I really shouldn't need to:
In strongly-typed functional programming all I need to know is the type of my inputs to know what type of output I get and whether I should expect side effects. I might need to understand the workings of the function to know what value to expect, but not what type and whether any errors or other side effects will happen. The classical example is dividing by zero, where in an imperative language it may throw and error and stop the program, but in a functional language it will return a value so regardless of whether you understand division or not you know whether the program will continue running, throw and error, and what kind of value it will return.
I saw that Svg.Events.on was added to exported functions in b0ed4d9 however, the version installed through elm package install elm-lang/svg
does not export the function.
I need it to define my own decoder for onMouseMove events. Or is there another way to obtain the coordinates otherwise?
Is there any way to decode an SVG file (fetched as text) into the SVG nodes in this package? If not, are there any plans to add this?
There is a discrepancy in how elm-lang/html and elm-lang/svg packages work with style
attribute. This attribute is declared with different types in each package:
Html.Attributes.style : List (String, String) -> Attribute msg
versus
Svg.Attributes.style : String -> Attribute msg
This is not problem per se. What I find problematic though is that when multiple style
attributes are present the list of attributes of one element, in Html the styles are combined, whereas in Svg the "last one wins". See the following code fragment on ellie for illustration of the issue.
This typically causes problems when you have reusable view function and want to provide additional attributes like so:
myWidget : List (Attribute msg) -> Svg msg
myWidget additionalAttributes = ...
Wouldn't it make sense to unify this and have Svg.Attributes.style
have the same type and behavior as Html.Attributes.style
?
See an example fix I had to do in a project I'm working on to work around this problem.
When attempting to add a class name to an SVG element using class
from the elm-lang/html
package the compiler doesn't complain. When running the compiled program an error is thrown:
Uncaught TypeError: Cannot set property className of #<SVGElement> which has only a getter
It's a stupid mistake, but still, when working with some SVG content nested in an HTML view it's not unlikely to happen.
Below is a sample program that produces the error.
module Main exposing (..)
import Html.App as Html
import Svg exposing (..)
import Html.Attributes exposing (class)
view : () -> Svg Never
view model =
svg []
[ g [ class "html-class" ]
[]
]
main : Program Never
main =
Html.beginnerProgram { model = (), view = view, update = \_ _ -> () }
totally related to https://github.com/elm-lang/svg/issues/22 but i'm adding this for more visibility. I'm seeing bugs in chrome that may be related to xlink:href and can't easily add href to test.
How to fix that ?
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0"
},
"indirect": {
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.3"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}
Would you like me to update your elm.json accordingly? [Y/n]: y
-- PROBLEM BUILDING DEPENDENCIES -----------------------------------------------
I ran into a compilation error when trying to build the following package:
elm/svg 1.0.1
This probably means it has package constraints that are too wide. It may be
possible to tweak your elm.json to avoid the root problem as a stopgap. Head
over to https://elm-lang.org/community to get help figuring out how to take this
path!
Note: To help with the root problem, please report this to the package author
along with the following information:
elm/core 1.0.5
elm/html 1.0.0
elm/json 1.1.3
elm/virtual-dom 1.0.3
If you want to help out even more, try building the package locally. That should
give you much more specific information about why this package is failing to
build, which will in turn make it easier for the package author to fix it!
It seems VirtualDom.Events.onWithOptions
works well on SVG elements too but currently it is not exposed by Svg.Events
module. I'm not in a hurry but beginners don't know this function is available via virtual-dom
package and they spends their time to find it. (I hope this issue helps them.)
Hi,
So there is one quircky thing about SVG when you are trying to get precise information about the position of pointer inside the SVG itself. Namely when you bind event handler to e.g. mousemove
you would get for clientX, and clientY with values relative to the document, not the node I have bound my handler to.
This is somewhat normal because SVG uses different units potentially for internal drawing, and you need to transform those units into pixels relative to the SVG it self. Here is how you do it in dirty JS world:
function handler(e){
var svg = document.getElementsByTagName("svg")[0]
svg = document.querySelector('svg');
var pt = svg.createSVGPoint();
pt.x = e.clientX;
pt.y = e.clientY;
return pt.matrixTransform(svg.getScreenCTM().inverse());
};
I just wanted to leave a note about this. I have no idea how to fix this nicely.
My quick and dirty solution was to make native module, and stick similar code into it.
Better systematic solution requires much forethought, which I believe you can allocate to the issue. In mean time I will continue thinking about neat solution also :)
Further reading:
https://www.sitepoint.com/how-to-translate-from-dom-to-svg-coordinates-and-back-again/
With SVG 2, the xlink:
namespace is not necessary and attributes using it are deprecated.
I expect Svg.Attributes
should reflect this change by, for example, exposing href
instead of xlinkHref
.
Ditto for xlinkActuate, xlinkHref, xlinkRole, xlinkShow, xlinkTitle, xlinkType.
I believe the viewBox : Int -> Int -> Int -> Int -> Attribute msg
type signature would be more intutive than viewBox : String -> Attribute msg
.
Html.Attributes
has adaptations like this, such as Html.Attributes.spellcheck
, which uses the Bool -> Attribute msm
signature instead of the traditional String -> Attribute msg
.
The following program results in a blank Html page when built with elm-lang/svg 1.1.0:
module KeyedProblem exposing (..)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Svg.Keyed
import VirtualDom
import Json.Encode as Json
svgNamespace : Attribute msg
svgNamespace =
VirtualDom.property "namespace" (Json.string "http://www.w3.org/2000/svg")
main : Svg a
main =
svg
[ height "600"
, width "200"
]
[ Svg.Keyed.node "g"
[-- svgNamespace
]
[ ( "1", drawCircle 100 )
, ( "2", drawCircle 200 )
]
]
drawCircle : Int -> Svg a
drawCircle y =
circle
[ cx "30"
, cy (toString y)
, r "15"
, Svg.Attributes.style "fill: black"
]
[]
If I remove the comment chars and enable svgNamespace
as an attribute then the page displays the two circles as expected.
Hello!
I'm trying to center text around a given point, both horizontally and vertically (i.e. the baseline should be slightly below the point). Solutions I've found online generally involve creating the element, querying its bounding box, and then mutating the element to adjust, something which I don't believe is possible in elm/svg.
Is there a way to accomplish this in pure elm, or should I fall back on JS+ports instead?
Also, I apologize if this has already been asked or if this is an inappropriate place to ask. I've cursorily checked them elm discourse and also this issue board, and didn't find anything, but I might have missed something.
Thanks!
I'm looking for a way to serialize SVG. Related to elm/file#1
The app I’m using this for is pretty niche, so a lot of this might not make sense, but I’ll give it a go:
You can take a look at an early, in progress version at http://hexagra.ms/678978
Its an app for working with the ancient Chinese book of philosophy called the I Ching. The UI allows users to enter specific numbers that correlate with shapes that may be looked up in the book. I dynamically generate these shapes as SVG for display in the browser. However, I also want to allow users to download the SVG shapes to do whatever they’d like with (I use this feature myself for journaling purposes.) I currently use ports to accomplish this, but it’d be nice if I could keep everything in Elm by using elm/file
's download
function. The only missing piece of the puzzle appears to be the lack of ability to serialize SVG nodes.
This is admittedly a pretty niche use case, although it isn’t too much of a stretch to imagine someone building some sort of vector image editor/creator, and wanting to allow users to download the SVG content they’ve created in the browser, which is what I thought the example from the elm/file
documentation was describing, at least until I looked harder at the type signatures.
In Elm 18 the following code was working as intended:
g
[]
[ renderUI state
, Svg.Keyed.node "g"
[]
<|
List.map2 (prepareCellContext state)
(List.map getCellKey mapList)
(List.map (pointsToString << mapPolygonCorners << getCell) mapList)
]
Currently (Elm 19), this code puts the related DOM markup but the browsers (Chrome, Firefox) don't render it.
After some research I had to replace it with the low level VirtualDom.keyedNodeNS "http://www.w3.org/2000/svg"
which is working fine now:
g
[]
[ renderUI state
, VirtualDom.keyedNodeNS "http://www.w3.org/2000/svg"
"g"
[]
<|
List.map2 (prepareCellContext state)
(List.map getCellKey mapList)
(List.map (pointsToString << mapPolygonCorners << getCell) mapList)
]
Just as in HTML, it’s useful to be able to create a space-separated list of classes to be applied to an SVG element. Html.Attributes.classList
does this for HTML. For Svg
, we must implement this ourselves.
Happy to submit a PR with an implementation if this would be considered helpful.
Current as of Elm 0.18.
having to import and use tons of "x" "x1"s is extremly tedious, it would be better to provide special functions, which take, for example for a circle its center coordinates and radius (+generic attributes +child nodes).
This is similar to #22 but I thought I would add more details since this was actually causing problems with rendering.
Summary: Use of xlink:href
in SVG output causes rendering problems with the shadow DOM and IDs.
Minimal demo: An Ellie was created. This demo shows three examples of SVG. The structure of the SVG has common shapes (the note-heads, for example) defined once, and then re-used by reference in the body.
xlink:href
and renders using the Elm view, using the elm-svg-parser to turn a string into Html nodes; as you can see the note heads and other symbols do not appear. The shapes that are defined in-line (the staff lines and note stems) display fine.xlink:href
references have been replaced with just href
.innerHTML
directly in the JavaScript. The references are still xlink:href
. This displays correctly.The best I can figure is that the use of xlink:href
does not work with the Shadow DOM or the Elm DOM diffing system; only href
works. Outside of the Shadow DOM, it works fine.
The following screenshot shows the output of the DOM; As you can see, in the second and third examples, the Shadow DOM node correctly 'points' to the shared symbol definition and shows it as a child.
Note that this isn't necessarily an issue with the SVG generator in Elm, and the application that generates the SVG examples here is being updated to support non-namespaced href attributes. But since #22 is still open and gathering use cases and examples, I thought it would be good to add another account of my experiences.
i'm trying to write a function that takes an int and outputs svg text like so:
drawNum num =
text' [ x 30, y 30 ] [ text (toString num) ]
the documentation says text'
outputs Svg Msg
so I wrote the type annotation like
drawNum : Int -> Svg Msg
but i keep getting this error:
The type annotation is saying:
Int -> Svg a
But I am inferring that the definition has this type:
Float -> Svg a
I checked elm-repl
to see what the type annotation of Svg.text'
looks like and saw this:
> import Svg
> Svg.text'
<function> : List (Svg.Attribute a) -> List (Svg.Svg a) -> Svg.Svg a
I'm getting similar results for text
too.. I guess I'm not understanding exactly what Svg a
is in relation to Svg msg
? Also why is elm insisting that I am passing in a Float
?
Hello, and sorry if I somehow missed the explainations somewhere but I can't seem to find informations on how to use onResize.
When I add onResize to an svg image with width "100%" and height "100%", the even doesn't seem to register (I've added a simple switch to change a value displayed and it doesn't change).
I'd be really glad to have your thoughts on this. Thanks in advance ^^
Assume I have:
svgView1 : Svg Msg1 -- from one component
svgView2 : Svg Msg2 -- from another component
and want to combine them:
type Msg = One Msg1 | Another Msg2
combinedView : Html Msg
combinedView = svg [] [ Svg.map One svgView1, Svg.map Another svgView2 ]
How should that be done without Svg.map
?
Note that doing it "on the outside" with Html.App.map
is conceptually not possible, because we need to "unify" the types of svgView1
and svgView2
already before putting them into the argument list for svg
. So the type mapping has to happen on the Svg
, not Html
, level.
I know that VirtualDom.map
could be used, or its alias Html.App.map
, even on the Svg
level, but that looks accidental. Should be better with Svg.map
.
I'm just trying to build the example clock SVG program in the Elm Guide, but I get an obscure error:
~/code/elm/tut/guide ᐅ elm-package install elm-lang/svg
Packages configured successfully!
~/code/elm/tut/guide ᐅ elm-make clock.elm
Success! Compiled 12 modules.
elm-make: elm-stuff/build-artifacts/0.17.0/elm-lang/svg/1.1.1/Svg.elmo: openFile: does not exist (No such file or directory)
The Elm Guide also doesn't explain how to install the library (I guessed that elm-package install elm-lang/svg
was needed), but that's another story,
Howdy,
I recently added svg support to a module. I was already exposing all of HTML, so I added svg support function-by-function until the code compiled. After compilation, to my surprise, no svg was rendered. The reason for this, it turns out, is that the code compiled using the svg function from Html. It feels to me like this kind of substitution should either not compile or work. Any thoughts?
Here is a reduced version of the code that demonstrates this issue:
import Html exposing (..)
import Html.Attributes as HA exposing (..)
import Svg exposing (circle, polygon, rect)
import Svg.Attributes as SA exposing (viewBox, id,x, y, width, height, rx, ry, fill, cx, cy, r, version, points, transform)
main = svg
[ SA.id "center", version "1.1", x "0", y "0", viewBox "0 0 323.141 322.95"
]
[ polygon [ fill "#F0AD00", points "161.649,152.782 231.514,82.916 91.783,82.916" ] []
, polygon [ fill "#7FD13B", points "8.867,0 79.241,70.375 232.213,70.375 161.838,0" ] []
, rect
[ fill "#7FD13B", x "192.99", y "107.392", SA.width "107.676", SA.height "108.167"
, transform "matrix(0.7071 0.7071 -0.7071 0.7071 186.4727 -127.2386)"
]
[]
, polygon [ fill "#60B5CC", points "323.298,143.724 323.298,0 179.573,0" ] []
, polygon [ fill "#5A6378", points "152.781,161.649 0,8.868 0,314.432" ] []
, polygon [ fill "#F0AD00", points "255.522,246.655 323.298,314.432 323.298,178.879" ] []
, polygon [ fill "#60B5CC", points "161.649,170.517 8.869,323.298 314.43,323.298" ] []
]
```
Wouldn't it be better for a statically typed language such as Elm to encode attribute values with appropriate types rather than using strings for most things?
I'm thinking especially of paths being defined as strings.
If you pass a blank node name to the Svg.node
method it will compile, but will crash the browser.
import Svg
Svg.node "" [] []
Results in:
Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.
at _VirtualDom_applyPatch (app.js:3759:13)
at _VirtualDom_applyPatchesHelp (app.js:3717:17)
at _VirtualDom_applyPatches (app.js:3708:9)
at app.js:4081:16
at updateIfNeeded (app.js:4615:56)
It looks like this also results in an infinite loop, since the number of errors keeps going until you refresh the page.
[ svg
[ attribute "xmlns" "http://www.w3.org/2000/svg"
, SA.viewBox "-11716.94 19034.5 24.44 24.441"
, SA.class "svg-icon svg-icon-snapshot-card"
]
[ Svg.defs []
[ Svg.style [] [ Svg.text ".a{fill:none;stroke:#000;stroke-width:1px;}" ] ]
, Svg.g [ SA.transform "rotate(90 -15363.5 3671)" ]
[ Svg.path
[ SA.class "a"
, SA.d "M23.5 15L17 17l-2 6.5L.5 9V.5H9z"
]
[]
, Svg.circle
[ SA.class "a"
, SA.cx "2"
, SA.cy "2"
, SA.r "2"
, SA.transform "translate(3.5 3.5)"
]
[]
]
]
]
When inspecting in chrome, viewbox
comes out as lowercase, doesn't render the svg. When I edit any attribute of the svg, chrome fixes it to camelcase viewBox
and the svg shows
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.