MacCASOutreach / graphicsvg / GraphicSVG.Widget

Include GraphicSVG animations and functionality within your own elm/html app! Your existing code can utilize as many widgets as it likes; each one receives its own co-ordinate system and view!

See GitHub for a full example of widgets embedded inside Elm Html.

Static Icons

Static icons can render shapes without requiring you to modify your models and messages to include special things, as in the case of the Interactive Widgets below. Use this when you don't need the user to interact with the shapes (i.e. you don't need to receieve any messages from the shape). For example, avatars and icons can use this.

icon : String -> Basics.Float -> Basics.Float -> List (GraphicSVG.Shape Basics.Never) -> Html a

A static graphic that returns no messages. Use this when you don't need to handle messages about clicking shapes, getting mouse positions, etc. This function is simpler and requires no other modifications to your model and message types.

Then, use icon as follows in your view function:

icon "Circle" 50 50
    [
        circle 5
            |> filled blue
    ]

Note that including any of the notify* functions will result in a type error. This is because the type Shape Never is a shape which can never send any messages. This is great! It means you can't compile with messages accidentally. It also means that, for compatibility with older code, you might need to change that code's type signature to be more generic (e.g. Shape a). And, for older code with any notify*, you'll have to use a proper Widget (below), even if you don't plan on using the messages.

Interactive Widgets

Basic Types

The basic message and model types must be wrapped and contained within your app's message type and model type respectively. These are mostly opaque data types of which you do not need to know the gritty details. Simply include them in your app and you're all set!


type Msg

The messages a widget can send and receive. It is only necessary to pipe these by including them in your message type. Ensure that you send each and every message received in your main update function to the Widget.update function to maintain consistency. See the above discussion under Wrapper for a more detailed idea of how to use this.


type alias Model =
{ cw : Basics.Float
, ch : Basics.Float
, ww : Basics.Float
, wh : Basics.Float
, id : String 
}

The state of a given widget. This is considered an opaque type; as such it is not necessary to know what is inside for your app to function. It stores information about the size of the widget and the co-ordinate system for purposes of rendering and sending messages with mouse position. It must be kept up to date using subscriptions and commands, and can be created with the init function. This is most often included in your app's model, e.g.

-- your app's Model type
type alias Model = 
    {
        ...
    ,   widgetState : Widget.Model
        ...
    }

Several of these could easily be saved in a structure such as a dictionary to maintain the state of several widgets at once!

Initialization

Some helper functions for initializing your widget(s).

init : Basics.Float -> Basics.Float -> String -> ( Model, Platform.Cmd.Cmd Msg )

The init function takes the size of the widget as well as the wrapper message and a unique string identifier of the widget. You must ensure that the identifier is unique, otherwise undefined behaviour can occur related to notification functions (especially the *At functions). You are free to change the size or idenfier at any time throughout the execution of your program, simply by calling init again and storing the model. However, note that each time init is called for a given widget, its accompanying command must also be executed.

Note: You should issue the command given by init each time the widget becomes visible again after being offscreen, to ensure that the co-ordinate system is kept up-to-date. Therefore, storing the command for use later is a good idea.

subscriptions : Platform.Sub.Sub Msg

Generate a subscription related to each widget in your app. It must be active whenever the widget in question is onscreen. It can be active when the widget is not onscreen, however this is not necessary and may be a waste of resources.

Updating a widget model

update : Msg -> Model -> ( Model, Platform.Cmd.Cmd Msg )

Helper function to update the state of the widget. This can be considered a black box and should be used to simply update the Model stored inside of your app's model. Be sure to pipe every message through this update, to ensure the consistency of the widget's model and that you receive all notifications coming from the widget.

Rendering a widget

view : Model -> List (GraphicSVG.Shape userMsg) -> Html userMsg

Helper function which takes the widget's opaque model as well as a list of shapes to include in the widget. Widgets are compatible with all notification functions available in GraphicSVG. Each widget is given its own co-ordinate system defined by the widget's model. The *At notification functions return points relative to the widget (as long as the user has properly piped together the commands and subscriptions to their copy of Widget.Model. Widgets are clipped so they fit the aspect ratio defined in the widget's model.

Custom view

When the default resizing behaviour of the widget is limiting, use the functions from this section.


type ViewOption userMsg

Options to set a Widget's attributes in a more constrained way.

defaultViewOption : List (ViewOption userMsg)

The default view options where a widget resizes to the size of the container it's inside of. This is the view option used in the non-custom view function.

noViewOption : List (ViewOption userMsg)

A type of view that sets no style attributes limiting the size of the widget as part of your Html.

viewCustom : List (ViewOption userMsg) -> Model -> List (GraphicSVG.Shape userMsg) -> Html userMsg

Like view, but with the ability to set view options that determine how the widget fits into your existing Html.

See view for more details.