noredink-ui/styleguide-app/Main.elm

350 lines
10 KiB
Elm
Raw Normal View History

2022-03-15 21:06:13 +03:00
module Main exposing (main)
2018-02-13 00:32:38 +03:00
2021-11-05 21:26:43 +03:00
import Accessibility.Styled as Html exposing (Html)
import Browser exposing (Document, UrlRequest(..))
2020-03-24 22:26:41 +03:00
import Browser.Dom
import Browser.Navigation exposing (Key)
2022-03-30 21:15:14 +03:00
import Category exposing (Category)
import Css exposing (..)
import Css.Media exposing (withMedia)
import Dict exposing (Dict)
import Example exposing (Example)
import Examples
2022-03-15 21:06:13 +03:00
import Html.Styled.Attributes exposing (..)
2022-03-29 00:50:12 +03:00
import Http
import Json.Decode as Decode
2020-11-06 21:51:33 +03:00
import Nri.Ui.CssVendorPrefix.V1 as VendorPrefixed
import Nri.Ui.Heading.V2 as Heading
import Nri.Ui.MediaQuery.V1 exposing (mobile, notMobile)
import Nri.Ui.Page.V3 as Page
2022-03-07 23:26:56 +03:00
import Nri.Ui.SideNav.V2 as SideNav
import Nri.Ui.Sprite.V1 as Sprite
2022-03-15 21:06:13 +03:00
import Routes exposing (Route)
import Sort.Set as Set
2020-03-24 22:26:41 +03:00
import Task
import Url exposing (Url)
2018-02-13 00:32:38 +03:00
main : Program () Model Msg
2018-02-13 00:32:38 +03:00
main =
Browser.application
2018-02-13 00:32:38 +03:00
{ init = init
, update = update
, subscriptions = subscriptions
, view = view
, onUrlRequest = OnUrlRequest
, onUrlChange = OnUrlChange
2018-02-13 00:32:38 +03:00
}
type alias Model =
{ -- Global UI
route : Route
, previousRoute : Maybe Route
, moduleStates : Dict String (Example Examples.State Examples.Msg)
, navigationKey : Key
, elliePackageDependencies : Result Http.Error (Dict String String)
}
init : () -> Url -> Key -> ( Model, Cmd Msg )
2018-12-05 21:56:04 +03:00
init () url key =
( { route = Routes.fromLocation url
, previousRoute = Nothing
, moduleStates =
Dict.fromList
(List.map (\example -> ( example.name, example )) Examples.all)
2018-12-05 21:56:04 +03:00
, navigationKey = key
, elliePackageDependencies = Ok Dict.empty
2018-02-13 00:32:38 +03:00
}
2022-03-29 00:50:12 +03:00
, Cmd.batch
[ loadPackage
, loadApplicationDependencies
]
2018-02-13 00:32:38 +03:00
)
type Msg
= UpdateModuleStates String Examples.Msg
| OnUrlRequest Browser.UrlRequest
| OnUrlChange Url
| ChangeRoute Route
2020-03-24 22:26:41 +03:00
| SkipToMainContent
2022-03-29 00:50:12 +03:00
| LoadedPackages (Result Http.Error (Dict String String))
| NoOp
update : Msg -> Model -> ( Model, Cmd Msg )
update action model =
case action of
UpdateModuleStates key exampleMsg ->
case Dict.get key model.moduleStates of
Just example ->
example.update exampleMsg example.state
|> Tuple.mapFirst
(\newState ->
{ model
| moduleStates =
Dict.insert key
{ example | state = newState }
model.moduleStates
}
)
|> Tuple.mapSecond (Cmd.map (UpdateModuleStates key))
Nothing ->
( model, Cmd.none )
OnUrlRequest request ->
case request of
Internal loc ->
( model, Browser.Navigation.pushUrl model.navigationKey (Url.toString loc) )
External loc ->
( model, Browser.Navigation.load loc )
OnUrlChange route ->
( { model
| route = Routes.fromLocation route
, previousRoute = Just model.route
}
, Cmd.none
)
ChangeRoute route ->
( model
, Browser.Navigation.pushUrl model.navigationKey
(Routes.toString route)
)
2020-03-24 22:26:41 +03:00
SkipToMainContent ->
( model
, Task.attempt (\_ -> NoOp) (Browser.Dom.focus "maincontent")
)
2022-03-29 00:52:06 +03:00
LoadedPackages newPackagesResult ->
2022-03-29 20:31:06 +03:00
let
-- Ellie gets really slow to compile if we include all the packages, unfortunately!
-- feel free to adjust the settings here if you need more packages for a particular example.
removedPackages =
[ "avh4/elm-debug-controls"
, "BrianHicks/elm-particle"
, "elm-community/random-extra"
, "elm/browser"
, "elm/http"
, "elm/json"
, "elm/parser"
, "elm/random"
, "elm/regex"
, "elm/svg"
, "elm/url"
, "elm-community/string-extra"
, "Gizra/elm-keyboard-event"
, "pablohirafuji/elm-markdown"
, "rtfeldman/elm-sorter-experiment"
, "tesk9/accessible-html-with-css"
, "tesk9/palette"
, "wernerdegroot/listzipper"
]
in
2022-03-29 00:52:06 +03:00
( { model
| elliePackageDependencies =
2022-03-29 20:31:06 +03:00
List.foldl (\name -> Result.map (Dict.remove name))
(Result.map2 Dict.union model.elliePackageDependencies newPackagesResult)
removedPackages
2022-03-29 00:52:06 +03:00
}
2022-03-29 00:50:12 +03:00
, Cmd.none
)
NoOp ->
( model, Cmd.none )
subscriptions : Model -> Sub Msg
subscriptions model =
Dict.values model.moduleStates
|> List.map (\example -> Sub.map (UpdateModuleStates example.name) (example.subscriptions example.state))
|> Sub.batch
view : Model -> Document Msg
view model =
2020-06-19 23:52:02 +03:00
let
findExampleByName name =
Dict.values model.moduleStates
|> List.filter (\m -> m.name == name)
|> List.head
toBody view_ =
List.map Html.toUnstyled
[ view_
, Html.map never Sprite.attach
]
in
case model.route of
Routes.Doodad doodad ->
case findExampleByName doodad of
Just example ->
{ title = "Style Guide"
, body =
viewExample model example
|> Html.map (UpdateModuleStates example.name)
|> toBody
}
Nothing ->
{ title = "Style Guide"
, body = toBody notFound
}
Routes.Category category ->
{ title = "Style Guide"
, body = toBody (viewCategory model category)
}
Routes.All ->
{ title = "Style Guide"
, body = toBody (viewAll model)
}
2022-03-30 21:01:16 +03:00
viewExample : Model -> Example a msg -> Html msg
viewExample model example =
Html.div [ css [ maxWidth (Css.px 1400), margin auto ] ]
[ Example.view model.previousRoute
{ packageDependencies = model.elliePackageDependencies }
example
]
2022-03-30 21:13:11 +03:00
notFound : Html Msg
notFound =
Page.notFound
{ link = ChangeRoute Routes.All
, recoveryText = Page.ReturnTo "Component Library"
}
2022-03-30 21:15:58 +03:00
viewAll : Model -> Html Msg
viewAll model =
withSideNav model.route
[ mainContentHeader "All"
, viewPreviews "all" (Dict.values model.moduleStates)
]
2022-03-30 21:15:14 +03:00
viewCategory : Model -> Category -> Html Msg
viewCategory model category =
withSideNav model.route
[ mainContentHeader (Category.forDisplay category)
, model.moduleStates
|> Dict.values
|> List.filter
(\doodad ->
Set.memberOf
(Set.fromList Category.sorter doodad.categories)
category
)
|> viewPreviews (Category.forId category)
]
withSideNav : Route -> List (Html Msg) -> Html Msg
withSideNav currentRoute content =
2020-06-20 00:12:53 +03:00
Html.div
[ css
[ displayFlex
, withMedia [ mobile ] [ flexDirection column, alignItems stretch ]
2020-06-20 00:12:53 +03:00
, alignItems flexStart
2021-12-03 22:40:57 +03:00
, maxWidth (Css.px 1400)
, margin auto
2020-06-20 00:12:53 +03:00
]
]
[ navigation currentRoute
2021-11-05 21:26:43 +03:00
, Html.main_
[ css
[ flexGrow (int 1)
, margin2 (px 40) zero
, Css.minHeight (Css.vh 100)
]
]
content
]
mainContentHeader : String -> Html msg
mainContentHeader heading =
Heading.h1
[ Heading.customAttr (id "maincontent")
, Heading.customAttr (tabindex -1)
, Heading.css [ marginBottom (px 30) ]
]
[ Html.text heading ]
viewPreviews : String -> List (Example state msg) -> Html Msg
viewPreviews containerId examples =
examples
|> List.map (\example -> Example.preview ChangeRoute example)
|> Html.div
[ id containerId
, css
[ Css.displayFlex
, Css.flexWrap Css.wrap
, Css.property "gap" "10px"
]
]
2021-05-28 04:23:07 +03:00
navigation : Route -> Html Msg
navigation currentRoute =
let
2021-12-04 00:34:32 +03:00
categoryNavLinks : List (SideNav.Entry Route Msg)
categoryNavLinks =
List.map
(\category ->
SideNav.entry (Category.forDisplay category)
[ SideNav.href (Routes.Category category) ]
)
Category.all
in
2021-12-03 19:45:06 +03:00
SideNav.view
2022-03-07 23:26:56 +03:00
{ isCurrentRoute = (==) currentRoute
, routeToString = Routes.toString
2021-12-03 19:45:06 +03:00
, onSkipNav = SkipToMainContent
, css =
[ withMedia [ notMobile ]
[ VendorPrefixed.value "position" "sticky"
, top (px 55)
]
]
2021-12-03 19:45:06 +03:00
}
2021-12-04 00:34:32 +03:00
(SideNav.entry "All" [ SideNav.href Routes.All ]
:: categoryNavLinks
)
2022-03-29 00:50:12 +03:00
loadPackage : Cmd Msg
loadPackage =
Http.get
{ url = "/package.json"
, expect =
Http.expectJson
LoadedPackages
(Decode.map2 Dict.singleton
(Decode.field "name" Decode.string)
(Decode.field "version" Decode.string)
)
}
loadApplicationDependencies : Cmd Msg
loadApplicationDependencies =
Http.get
{ url = "/application.json"
, expect =
Http.expectJson
LoadedPackages
(Decode.at [ "dependencies", "direct" ] (Decode.dict Decode.string))
}