In this image, the panel to the left is called the sidebar and the page selected in it is shown in the remaining space to the right.
Note that this package is built primarily for UI created with mdgriffith/elm-ui
.
You can still use elm/html
with Element.html
though.
application : ApplicationConfig (Msg pageMsg) flags -> PageBuilder pageModel pageMsg flags -> Platform.Program Json.Decode.Value (Model pageModel flags) (Msg pageMsg)
Here we create our UI explorer app.
import MyCoolUi
import UiExplorer
pages =
UiExplorer.firstPage
"Button"
(UiExplorer.static MyCoolUi.button)
|> UiExplorer.nextPage
"Footer"
(UiExplorer.static MyCoolUi.footer)
|> UiExplorer.nextPage
"Login Form"
{ init = MyCoolUi.loginInit
, update = MyCoolUi.loginUpdate
, view =
\pageSize model ->
MyCoolUi.loginView model
, subscriptions = always Sub.none
}
main =
UiExplorer.application
UiExplorer.defaultConfig
pages
Note that we didn't add type signatures for pages
and main
in the example.
If we did, we'd have to update it every time we add a new page and the type signatures would get messy.
Instead it's best to just let the compiler infer it automatically.
defaultConfig : ApplicationConfig msg ()
The default application configuration.
{ flagsDecoder = Decode.succeed ()
, layoutOptions = []
, layoutAttributes = []
, relativeUrlPath = []
, title = Element.text "UI explorer"
}
{ flagsDecoder : Json.Decode.Decoder flags
, layoutOptions : List Element.Option
, layoutAttributes : List (Element.Attribute msg)
, relativeUrlPath : List String
, sidebarTitle : Element msg
}
These are settings we can change when creating our UI explorer application.
flagsDecoder
lets us parse json flags we pass to our app. This gets passed along to the init function in our pages (or the view function if you're creating a static page).layoutOptions
and layoutAttributes
are used in our app's Element.layoutWith to control things like the default font or focusStyle.relativeUrlPath
sets the relative path to pages. relativeUrlPath = []
makes the page link look like this https://localhost/MyPage, and relativeUrlPath = [ ui, here ]
produces https://localhost/ui/here/MyPageA "page" is something you can select in the sidebar to display when the app is running. Pages can contain a single widget, tables showing every variation of your button components, or an entire login page. It's up to you!
firstPage : String -> Page model msg flags -> PageBuilder ( (), model ) (PageMsg () msg) flags
The first page in your UI explorer. This is the default page if the user doesn't specify a url path.
import Element
import UiExplorer
pages =
UiExplorer.firstPage
"My first page"
(UiExplorer.static (\_ _ -> Element.text "Hi!"))
nextPage : String -> Page model msg flags -> PageBuilder modelPrevious msgPrevious flags -> PageBuilder ( modelPrevious, model ) (PageMsg msgPrevious msg) flags
Additional pages in your UI explorer.
You have to start with firstPage
before chaining the result to nextPage
s.
Each page must also have a unique name.
import Element
import UiExplorer
pages =
UiExplorer.firstPage
"My first page"
(UiExplorer.static (\_ _ -> Element.text "Hi!"))
|> UiExplorer.nextPage
"My second page"
(UiExplorer.static (\_ _ -> Element.none))
groupPages : String -> (PageBuilder a0 b0 c0 -> PageBuilder a1 b1 c1) -> PageBuilder a0 b0 c0 -> PageBuilder a1 b1 c1
If your list of pages on the sidebar is starting to get too long, you can group some of them together with groupPages
.
import MyPages
import UiExplorer
flatPages =
UiExplorer.firstPage "Login" MyPage.login
|> UiExplorer.nextPage "About us" MyPage.aboutUs
|> UiExplorer.nextPage "Fonts" MyPage.fonts
|> UiExplorer.nextPage "Colors" MyPage.colors
|> UiExplorer.nextPage "Basic components" MyPage.basicComponents
|> UiExplorer.nextPage "Homepage" MyPage.homepage
nowItsGrouped =
UiExplorer.firstPage "Login" MyPage.login
|> UiExplorer.nextPage "About us" MyPage.aboutUs
|> UiExplorer.groupPages "Building blocks"
(UiExplorer.nextPage "Fonts" MyPage.fonts
>> UiExplorer.nextPage "Colors" MyPage.colors
>> UiExplorer.nextPage "Basic components" MyPage.basicComponents
)
|> UiExplorer.nextPage "Homepage" MyPage.homepage
Two things to note:
firstPage
having a different type signature from nextPage
, you can't place the first page in a group.
If this is a problem, create an issue on github explaining your use case.static : (PageSize -> flags -> Element msg) -> Page flags msg flags
A page that doesn't change or react to user input. It's just a view function.
getPagePaths : List String -> PageBuilder model msg flags -> List String
Get the relative paths to all the pages you've defined.
pages =
UiExplorer.firstPage "First page" (UiExplorer.static (\_ _ -> Element.none))
|> UiExplorer.nextPage "Second page" (UiExplorer.static (\_ _ -> Element.none))
paths =
-- [ "/First%20page", "/Second%20page" ]
getPagePaths [] pages
pathWithSubdomain =
-- [ "/subdomain/First%20page", "/subdomain/Second%20page" ]
getPagePaths [ "subdomain" ] pages
{ init : flags -> ( model
, Platform.Cmd.Cmd msg )
, update : msg -> model -> ( model
, Platform.Cmd.Cmd msg )
, view : PageSize -> model -> Element msg
, subscriptions : model -> Platform.Sub.Sub msg
}
All the functions you need for wiring together an interactive page. It's basically just Browser.element
.
import MyCoolUi
import UiExplorer
loginPage =
{ init = MyCoolUi.loginInit
, update = MyCoolUi.loginUpdate
, view = \pageSize model -> MyCoolUi.loginView model
, subscriptions = always Sub.none
}
pages =
UiExplorer.firstPage "Login Form" loginPage
{ width : Quantity Basics.Int Pixels
, height : Quantity Basics.Int Pixels
}
The size of the page your UI gets placed in.
This is not the same as Browser.Events.resize
since the UI explorer displays a sidebar that can take up some of the window space.
You'll need ianmackenzie/elm-units
in order to use Quantity Int Pixels
.
import Pixels
getWidth : PageSize -> Int
getWidth pageSize =
Pixels.inPixels pageSize.width