├── .gitignore ├── examples ├── pwa │ ├── assets │ │ └── prod │ │ │ ├── icons │ │ │ ├── 128.png │ │ │ ├── 144.png │ │ │ ├── 152.png │ │ │ ├── 16.png │ │ │ ├── 192.png │ │ │ ├── 256.png │ │ │ ├── 32.png │ │ │ ├── 512.png │ │ │ └── 64.png │ │ │ ├── fonts │ │ │ ├── OCRA.otf │ │ │ ├── RakutenSansUI_W_Bd.woff │ │ │ ├── RakutenSansUI_W_Bd.woff2 │ │ │ ├── RakutenSansUI_W_Blk.woff │ │ │ ├── RakutenSansUI_W_Blk.woff2 │ │ │ ├── RakutenSansUI_W_Lt.woff │ │ │ ├── RakutenSansUI_W_Lt.woff2 │ │ │ ├── RakutenSansUI_W_Rg.woff │ │ │ └── RakutenSansUI_W_Rg.woff2 │ │ │ └── images │ │ │ ├── form.png │ │ │ ├── r10.png │ │ │ ├── view.png │ │ │ ├── buttons.png │ │ │ ├── makers.png │ │ │ ├── palette.png │ │ │ ├── base_500.png │ │ │ ├── buttons300.png │ │ │ ├── examples.png │ │ │ ├── form-tree.png │ │ │ ├── examples-600.png │ │ │ ├── system-color.png │ │ │ ├── material-colors.png │ │ │ ├── okaimonopanda.gif │ │ │ ├── colors-overview1.png │ │ │ └── colors-overview400.png │ ├── src-elm-starter │ │ ├── Starter │ │ │ ├── Version.elm │ │ │ ├── Model.elm │ │ │ ├── ConfMain.elm │ │ │ ├── SnippetCss.elm │ │ │ ├── Icon.elm │ │ │ ├── FileNames.elm │ │ │ ├── Cache.elm │ │ │ ├── ConfMeta.elm │ │ │ ├── Manifest.elm │ │ │ ├── ServiceWorker.elm │ │ │ ├── ElmGo.elm │ │ │ └── SnippetJavascript.elm │ │ ├── String │ │ │ └── Conversions.elm │ │ ├── Worker.elm │ │ ├── Html │ │ │ └── String │ │ │ │ ├── Keyed.elm │ │ │ │ ├── Extra.elm │ │ │ │ └── Lazy.elm │ │ └── Application.elm │ ├── elm-analyse.json │ ├── review │ │ ├── elm.json │ │ └── src │ │ │ └── ReviewConfig.elm │ ├── elm.json │ ├── package.json │ └── src │ │ └── Pages │ │ ├── Counter.elm │ │ ├── Form_Example_PhoneSelector.elm │ │ └── Form_Entities.elm ├── simpleView │ ├── elm.json │ └── src │ │ └── Main.elm ├── simpleForm │ ├── elm.json │ └── src │ │ └── Main.elm └── creditCardForm │ └── elm.json ├── src └── R10 │ ├── Utils.elm │ ├── Table │ └── Internal │ │ ├── Msg.elm │ │ ├── Cell.elm │ │ ├── State.elm │ │ ├── Types.elm │ │ ├── Style.elm │ │ ├── Svg.elm │ │ ├── Config.elm │ │ ├── Sorter.elm │ │ └── Placeholder.elm │ ├── When.elm │ ├── Form │ ├── Internal │ │ ├── QtySubmitAttempted.elm │ │ ├── Dict.elm │ │ ├── TempSubmitButton.elm │ │ ├── ValidationCode.elm │ │ ├── Key.elm │ │ ├── StateForValues.elm │ │ ├── Msg.elm │ │ ├── MakerForValidationKeys.elm │ │ ├── State.elm │ │ └── MakerForValues.elm │ └── README.md │ ├── FormComponents │ └── Internal │ │ ├── UI │ │ ├── Const.elm │ │ └── Color.elm │ │ ├── Style.elm │ │ ├── Utils │ │ └── FocusOut.elm │ │ ├── TextColors.elm │ │ ├── ExtraCss.elm │ │ ├── IconButton.elm │ │ ├── Phone │ │ └── Common.elm │ │ └── Single │ │ └── Common.elm │ ├── Theme.elm │ ├── Link.elm │ ├── FontSize.elm │ ├── CountryCode.elm │ ├── Mode.elm │ ├── Transition.elm │ ├── Card.elm │ ├── Footer.elm │ ├── Translations.elm │ ├── Libu.elm │ ├── Svg │ └── Utils.elm │ ├── Paragraph.elm │ ├── ValidationDate.elm │ ├── Color │ ├── Utils.elm │ └── AttrsBorder.elm │ ├── LanguageSelector.elm │ ├── Alert.elm │ ├── Color.elm │ ├── FormDebug.elm │ └── DropDown.elm ├── LICENSE ├── elm.json ├── PUBLISHING.md ├── CHANGELOG.md ├── CONTRIBUTING.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | node_modules 3 | Elmjutsu* 4 | build 5 | elm.js -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/128.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/144.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/152.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/16.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/192.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/256.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/32.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/512.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/icons/64.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/OCRA.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/OCRA.otf -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/form.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/r10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/r10.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/view.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/buttons.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/makers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/makers.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/palette.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/base_500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/base_500.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/buttons300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/buttons300.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/examples.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/form-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/form-tree.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/examples-600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/examples-600.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/system-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/system-color.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/material-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/material-colors.png -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/okaimonopanda.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/okaimonopanda.gif -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/colors-overview1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/colors-overview1.png -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/Version.elm: -------------------------------------------------------------------------------- 1 | module Starter.Version exposing (version) 2 | 3 | 4 | version : String 5 | version = 6 | "0.0.1" 7 | -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Bd.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Bd.woff -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Bd.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Bd.woff2 -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Blk.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Blk.woff -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Blk.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Blk.woff2 -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Lt.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Lt.woff -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Lt.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Lt.woff2 -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Rg.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Rg.woff -------------------------------------------------------------------------------- /examples/pwa/assets/prod/fonts/RakutenSansUI_W_Rg.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/fonts/RakutenSansUI_W_Rg.woff2 -------------------------------------------------------------------------------- /examples/pwa/assets/prod/images/colors-overview400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rakutentech/r10/HEAD/examples/pwa/assets/prod/images/colors-overview400.png -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/Model.elm: -------------------------------------------------------------------------------- 1 | module Starter.Model exposing (Model) 2 | 3 | import Starter.Flags 4 | 5 | 6 | type alias Model = 7 | Starter.Flags.Flags 8 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/String/Conversions.elm: -------------------------------------------------------------------------------- 1 | module String.Conversions exposing (fromValue) 2 | 3 | import Json.Encode 4 | 5 | 6 | {-| Convert a Json.Decode.Value to a JSON String. 7 | -} 8 | fromValue : Json.Encode.Value -> String 9 | fromValue value = 10 | Json.Encode.encode 0 value 11 | -------------------------------------------------------------------------------- /src/R10/Utils.elm: -------------------------------------------------------------------------------- 1 | module R10.Utils exposing (..) 2 | 3 | import Regex 4 | 5 | 6 | userReplace : String -> (Regex.Match -> String) -> String -> String 7 | userReplace userRegex replacer string = 8 | case Regex.fromString userRegex of 9 | Nothing -> 10 | string 11 | 12 | Just regex -> 13 | Regex.replace regex replacer string 14 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Msg.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Msg exposing (Msg(..)) 2 | 3 | import R10.Form 4 | import R10.Table.Internal.Types 5 | 6 | 7 | type Msg 8 | = Sort String Bool 9 | | PaginatorNextPage 10 | | PaginatorPrevPage 11 | | PaginatorLengthOption Int 12 | | FiltersTogglePopup R10.Table.Internal.Types.Filter 13 | | FilterClear String 14 | | FiltersPopupFormMsg R10.Form.Msg 15 | -------------------------------------------------------------------------------- /src/R10/When.elm: -------------------------------------------------------------------------------- 1 | module R10.When exposing (do, otherwise, when) 2 | 3 | -- To be used as 4 | -- 5 | -- x = 10 == 10 6 | -- 7 | -- when x do ... otherwise ... 8 | 9 | 10 | type Do 11 | = Do 12 | 13 | 14 | type Otherwise 15 | = Otherwise 16 | 17 | 18 | do : Do 19 | do = 20 | Do 21 | 22 | 23 | otherwise : Otherwise 24 | otherwise = 25 | Otherwise 26 | 27 | 28 | when : Bool -> Do -> b -> Otherwise -> b -> b 29 | when a _ b _ c = 30 | if a then 31 | b 32 | 33 | else 34 | c 35 | -------------------------------------------------------------------------------- /examples/pwa/elm-analyse.json: -------------------------------------------------------------------------------- 1 | { 2 | "checks": { 3 | "ExposeAll": true, 4 | "SingleFieldRecord": false, 5 | "UnnecessaryParens": false, 6 | "ImportAll": false, 7 | "UseConsOverConcat": false, 8 | "MultiLineRecordFormatting": false, 9 | "MapNothingToNothing": false, 10 | "TriggerWords": false, 11 | "DropConcatOfLists": false 12 | }, 13 | "TriggerWords": { 14 | "words": ["test"] 15 | }, 16 | "excludedPaths": [ 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/ConfMain.elm: -------------------------------------------------------------------------------- 1 | module Starter.ConfMain exposing 2 | ( Conf 3 | , encoder 4 | ) 5 | 6 | import Json.Encode 7 | 8 | 9 | type alias Conf = 10 | { urls : List String 11 | , assetsToCache : List String 12 | } 13 | 14 | 15 | encoder : Conf -> Json.Encode.Value 16 | encoder mainConf_ = 17 | Json.Encode.object 18 | [ ( "urls", Json.Encode.list Json.Encode.string mainConf_.urls ) 19 | , ( "assetsToCache", Json.Encode.list Json.Encode.string mainConf_.assetsToCache ) 20 | ] 21 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Worker.elm: -------------------------------------------------------------------------------- 1 | port module Worker exposing (main) 2 | 3 | import Json.Encode 4 | import Starter.Conf 5 | import Starter.Flags 6 | import Starter.Model 7 | 8 | 9 | port dataFromElmToJavascript : Json.Encode.Value -> Cmd msg 10 | 11 | 12 | main : Program Starter.Flags.Flags Starter.Model.Model msg 13 | main = 14 | Platform.worker 15 | { init = \flags -> ( flags, dataFromElmToJavascript (Starter.Conf.conf_ flags) ) 16 | , update = \_ model -> ( model, Cmd.none ) 17 | , subscriptions = \_ -> Sub.none 18 | } 19 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/SnippetCss.elm: -------------------------------------------------------------------------------- 1 | module Starter.SnippetCss exposing (noJsAndLoadingNotifications) 2 | 3 | 4 | noJsAndLoadingNotifications : String -> String 5 | noJsAndLoadingNotifications classNotification = 6 | """ 7 | .""" ++ classNotification ++ """ 8 | { padding: 20px 9 | ; background-color: rgba(255,255,255,0.7) 10 | ; pointer-events: none 11 | ; color: black 12 | ; width: 100% 13 | ; text-align: center 14 | ; position: fixed 15 | ; left: 0 16 | ; z-index: 1 17 | ; box-sizing: border-box 18 | } 19 | """ 20 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/Icon.elm: -------------------------------------------------------------------------------- 1 | module Starter.Icon exposing 2 | ( iconFileName 3 | , iconsForManifest 4 | , iconsToBeCached 5 | ) 6 | 7 | 8 | iconsForManifest : List number 9 | iconsForManifest = 10 | [ 128, 144, 152, 192, 256, 512 ] 11 | 12 | 13 | iconsToBeCached : List number 14 | iconsToBeCached = 15 | [ 16, 32, 64 ] ++ iconsForManifest 16 | 17 | 18 | iconFileName : String -> Int -> String 19 | iconFileName relative size = 20 | let 21 | sizeString = 22 | String.fromInt size 23 | in 24 | relative ++ "/icons/" ++ sizeString ++ ".png" 25 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/QtySubmitAttempted.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.QtySubmitAttempted exposing 2 | ( QtySubmitAttempted 3 | , fromInt 4 | , increment 5 | , toInt 6 | ) 7 | 8 | 9 | type QtySubmitAttempted 10 | = QtySubmitAttempted Int 11 | 12 | 13 | toInt : QtySubmitAttempted -> Int 14 | toInt (QtySubmitAttempted int) = 15 | int 16 | 17 | 18 | fromInt : Int -> QtySubmitAttempted 19 | fromInt int = 20 | QtySubmitAttempted int 21 | 22 | 23 | increment : QtySubmitAttempted -> QtySubmitAttempted 24 | increment qtySubmitAttempted = 25 | fromInt (toInt qtySubmitAttempted + 1) 26 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/Dict.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.Dict exposing 2 | ( get 3 | , insert 4 | , update 5 | ) 6 | 7 | import Dict 8 | import R10.Form.Internal.Key 9 | 10 | 11 | get : R10.Form.Internal.Key.Key -> Dict.Dict String v -> Maybe v 12 | get key = 13 | Dict.get (R10.Form.Internal.Key.toString key) 14 | 15 | 16 | update : R10.Form.Internal.Key.Key -> (Maybe v -> Maybe v) -> Dict.Dict String v -> Dict.Dict String v 17 | update key = 18 | Dict.update (R10.Form.Internal.Key.toString key) 19 | 20 | 21 | insert : R10.Form.Internal.Key.Key -> v -> Dict.Dict String v -> Dict.Dict String v 22 | insert key = 23 | Dict.insert (R10.Form.Internal.Key.toString key) 24 | -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/UI/Const.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.UI.Const exposing 2 | ( inputTextFilledDown 3 | , inputTextFontSize 4 | , inputTextHeight 5 | ) 6 | 7 | import R10.FontSize 8 | 9 | 10 | inputTextHeight : number 11 | inputTextHeight = 12 | -- This need to be at least 50 otherwise the 13 | -- Chrome auto-suggestion functionality 14 | -- is covering hiding the lower border 15 | -- 16 | -- https://jira.rakuten-it.com/jira/browse/OMN-1936 17 | -- 18 | 50 19 | 20 | 21 | inputTextFontSize : Int 22 | inputTextFontSize = 23 | R10.FontSize.normalAsInt 24 | 25 | 26 | inputTextFilledDown : number 27 | inputTextFilledDown = 28 | 8 29 | -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/Style.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.Style exposing (Style(..), default, fromString, toString) 2 | 3 | 4 | type Style 5 | = FloatingLabels 6 | | FixedLabels 7 | 8 | 9 | toString : Style -> String 10 | toString style = 11 | case style of 12 | FloatingLabels -> 13 | "floatingLabels" 14 | 15 | FixedLabels -> 16 | "fixedLabels" 17 | 18 | 19 | fromString : String -> Style 20 | fromString string = 21 | case string of 22 | "floatingLabels" -> 23 | FloatingLabels 24 | 25 | "fixedLabels" -> 26 | FixedLabels 27 | 28 | _ -> 29 | default 30 | 31 | 32 | default : Style 33 | default = 34 | FloatingLabels 35 | -------------------------------------------------------------------------------- /src/R10/Theme.elm: -------------------------------------------------------------------------------- 1 | module R10.Theme exposing (Theme, fromFlags) 2 | 3 | {-| This is how we store information about the primary color used and if the site is in light or dark mode. 4 | 5 | Most of the application use only one primary color but some are configurable and can be used with different colors. 6 | 7 | @docs Theme, fromFlags 8 | 9 | -} 10 | 11 | import R10.Color.Internal.Primary 12 | import R10.Mode 13 | 14 | 15 | {-| -} 16 | type alias Theme = 17 | { mode : R10.Mode.Mode 18 | , primaryColor : R10.Color.Internal.Primary.Color 19 | } 20 | 21 | 22 | {-| Usually `mode` and `primaryColor` are stored in the flags. Use this helper to create a `Theme` from flags. 23 | -} 24 | fromFlags : 25 | { a 26 | | mode : R10.Mode.Mode 27 | , primaryColor : R10.Color.Internal.Primary.Color 28 | } 29 | -> Theme 30 | fromFlags flags = 31 | { mode = flags.mode 32 | , primaryColor = flags.primaryColor 33 | } 34 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/FileNames.elm: -------------------------------------------------------------------------------- 1 | module Starter.FileNames exposing 2 | ( FileNames 3 | , fileNames 4 | ) 5 | 6 | 7 | type alias FileNames = 8 | { outputCompiledJs : String 9 | , outputCompiledJsProd : String 10 | , indexHtml : String 11 | , manifestJson : String 12 | , redirects : String 13 | , robotsTxt : String 14 | , serviceWorker : String 15 | , sitemap : String 16 | , snapshot : String 17 | } 18 | 19 | 20 | fileNames : String -> String -> FileNames 21 | fileNames version commit = 22 | { manifestJson = "/manifest.json" 23 | , redirects = "/_redirects" 24 | , robotsTxt = "/robots.txt" 25 | , outputCompiledJs = "/elm.js" 26 | , outputCompiledJsProd = "/elm-" ++ version ++ "-" ++ commit ++ ".min.js" 27 | , indexHtml = "/index.html" 28 | , serviceWorker = "/service-worker.js" 29 | , sitemap = "/sitemap.txt" 30 | , snapshot = "/snapshot.jpg" 31 | } 32 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/Cache.elm: -------------------------------------------------------------------------------- 1 | module Starter.Cache exposing (stuffToCache) 2 | 3 | import Main 4 | import Starter.FileNames 5 | 6 | 7 | stuffToCache : String -> String -> String -> List ( String, String ) -> List ( String, String ) 8 | stuffToCache relative version commit assets = 9 | let 10 | fileNames = 11 | Starter.FileNames.fileNames version commit 12 | in 13 | [] 14 | -- Production elm.js 15 | ++ [ ( relative ++ fileNames.outputCompiledJsProd, version ) ] 16 | -- manifest.json 17 | ++ [ ( relative ++ fileNames.manifestJson, version ) ] 18 | -- Static pages coming from src/Main.elm 19 | ++ List.map (\url -> ( url, version )) Main.conf.urls 20 | -- Extra stuff coming from src/Main.elm 21 | ++ List.map (\url -> ( url, version )) Main.conf.assetsToCache 22 | ++ assets 23 | |> List.map (\( url, hash ) -> ( String.replace "//" "/" url, hash )) 24 | -------------------------------------------------------------------------------- /src/R10/Link.elm: -------------------------------------------------------------------------------- 1 | module R10.Link exposing (attrs, attrsUnderline) 2 | 3 | {-| 4 | 5 | @docs attrs, attrsUnderline 6 | 7 | -} 8 | 9 | import Element.WithContext exposing (..) 10 | import Element.WithContext.Font as Font 11 | import R10.Color.AttrsFont 12 | import R10.Context exposing (..) 13 | import R10.Transition 14 | 15 | 16 | {-| Attributes for links, useful if you need to make some text to render as if it was a link. 17 | -} 18 | attrs : List (Attr (R10.Context.ContextInternal z) () msg) 19 | attrs = 20 | [ R10.Color.AttrsFont.link 21 | , mouseOver [ R10.Color.AttrsFont.linkOver ] 22 | , transition 23 | ] 24 | 25 | 26 | {-| -} 27 | attrsUnderline : List (Attr (R10.Context.ContextInternal z) () msg) 28 | attrsUnderline = 29 | [ Font.underline 30 | , R10.Color.AttrsFont.link 31 | , mouseOver [ R10.Color.AttrsFont.linkOver ] 32 | , transition 33 | ] 34 | 35 | 36 | transition : Attribute (R10.Context.ContextInternal z) msg 37 | transition = 38 | R10.Transition.transition "color .2s ease-out, background-color .2s ease-out" 39 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Cell.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Cell exposing (simpleCell) 2 | 3 | import Element.WithContext exposing (..) 4 | import Element.WithContext.Font as Font 5 | import R10.Context exposing (..) 6 | import R10.FormComponents.Internal.UI.Color 7 | import R10.Palette 8 | import R10.Table.Internal.Placeholder 9 | import R10.Table.Internal.Style 10 | 11 | 12 | simpleCell : List (Attribute (R10.Context.ContextInternal z) msg) -> (Maybe data -> Maybe String) -> R10.Palette.Palette -> Maybe data -> Element (R10.Context.ContextInternal z) msg 13 | simpleCell attrs toStr palette maybeData = 14 | row 15 | (R10.Table.Internal.Style.defaultCellAttrs ++ attrs) 16 | (case toStr maybeData of 17 | Just str -> 18 | [ paragraph 19 | [ Font.color <| R10.FormComponents.Internal.UI.Color.onSurface palette ] 20 | [ text str ] 21 | ] 22 | 23 | Nothing -> 24 | [ R10.Table.Internal.Placeholder.view (R10.FormComponents.Internal.UI.Color.primary palette) [] ] 25 | ) 26 | -------------------------------------------------------------------------------- /examples/simpleView/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "avh4/elm-color": "1.0.0", 11 | "elm/browser": "1.0.2", 12 | "elm/core": "1.0.5", 13 | "elm/html": "1.0.0", 14 | "elm/json": "1.1.3", 15 | "elm/regex": "1.0.0", 16 | "elm/svg": "1.0.1", 17 | "elm/url": "1.0.0", 18 | "elm-community/string-extra": "4.0.1", 19 | "lucamug/elm-ui-with-context": "1.0.0", 20 | "mdgriffith/elm-ui": "1.1.8", 21 | "noahzgordon/elm-color-extra": "1.0.2", 22 | "zwilias/json-decode-exploration": "6.0.0" 23 | }, 24 | "indirect": { 25 | "elm/random": "1.0.0", 26 | "elm/time": "1.0.0", 27 | "elm/virtual-dom": "1.0.2", 28 | "fredcy/elm-parseint": "2.0.1", 29 | "mgold/elm-nonempty-list": "4.1.0" 30 | } 31 | }, 32 | "test-dependencies": { 33 | "direct": {}, 34 | "indirect": {} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/R10/Form/README.md: -------------------------------------------------------------------------------- 1 | TODO 2 | 3 | * FIX the problem of the duplcate helperSignin_M03_Submit 4 | * Use border color as primary color for the form library(?), also for links 5 | * FIx the view, so that the button is active at start R06 6 | * Check search input field in dropdowns 7 | * Fix fields in http://localhost:8002/#/r99 8 | * Green errors in password fields (wrong translations) 9 | 10 | 11 | 12 | ## Validations 13 | 14 | Validations are done 15 | 16 | * R10.Form.Internal.Update.onChangeValue (only for few fields, calling "runOnlyExistingValidations") 17 | * "validateEntireForm" instead run "runAllValidations", used when the submit buttons is pressed. 18 | 19 | 20 | ## Logic of fromFieldStateValidationToComponentValidation 21 | 22 | * R10.Form.Internal.Converter.fromFieldStateValidationToComponentValidation 23 | 24 | type alias ValidationSpecs = 25 | { showAlsoPassedValidation : Bool 26 | , pretendIsNotValidatedIfValid : Bool 27 | , validation : List Validation 28 | , validationIcon : R10.FormTypes.ValidationIcon 29 | } 30 | 31 | 32 | The main function for validations is R10.Form.Internal.Validation.validate 33 | 34 | 35 | shouldValidationBeVisible -------------------------------------------------------------------------------- /src/R10/Form/Internal/TempSubmitButton.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.TempSubmitButton exposing (button) 2 | 3 | import Element.WithContext exposing (..) 4 | import Element.WithContext.Background as Background 5 | import Element.WithContext.Border as Border 6 | import Element.WithContext.Font as Font 7 | import Element.WithContext.Input as Input 8 | import R10.Context exposing (..) 9 | import R10.Form 10 | import R10.Form.Internal.Msg 11 | import R10.Form.Internal.Update 12 | 13 | 14 | button : R10.Form.Form -> (R10.Form.Internal.Msg.Msg -> msg) -> Element (R10.Context.ContextInternal z) msg 15 | button form msgMapper = 16 | let 17 | submittable : Bool 18 | submittable = 19 | R10.Form.Internal.Update.isFormSubmittable form 20 | in 21 | map msgMapper <| 22 | Input.button 23 | [ padding 20 24 | , Border.rounded 6 25 | , Font.color <| rgb 1 1 1 26 | , Background.color <| 27 | if submittable then 28 | rgb 0 0.5 0 29 | 30 | else 31 | rgb 0.5 0 0 32 | ] 33 | { label = text "Submit" 34 | , onPress = Just <| R10.Form.Internal.Msg.Submit form.conf 35 | } 36 | -------------------------------------------------------------------------------- /src/R10/FontSize.elm: -------------------------------------------------------------------------------- 1 | module R10.FontSize exposing 2 | ( large, normal, small, xlarge, xxlarge, xsmall, xxsmall 3 | , normalAsInt 4 | ) 5 | 6 | {-| Font sizes 7 | 8 | @docs large, normal, small, xlarge, xxlarge, xsmall, xxsmall 9 | 10 | @docs normalAsInt 11 | 12 | -} 13 | 14 | import Element.WithContext exposing (..) 15 | import Element.WithContext.Font as Font 16 | import R10.Context exposing (..) 17 | 18 | 19 | {-| -} 20 | xlarge : Attr (R10.Context.ContextInternal z) decorative msg 21 | xlarge = 22 | Font.size 24 23 | 24 | 25 | {-| -} 26 | xxlarge : Attr (R10.Context.ContextInternal z) decorative msg 27 | xxlarge = 28 | Font.size 32 29 | 30 | 31 | {-| -} 32 | large : Attr (R10.Context.ContextInternal z) decorative msg 33 | large = 34 | Font.size 20 35 | 36 | 37 | {-| -} 38 | normalAsInt : Int 39 | normalAsInt = 40 | 16 41 | 42 | 43 | {-| -} 44 | normal : Attr (R10.Context.ContextInternal z) decorative msg 45 | normal = 46 | Font.size normalAsInt 47 | 48 | 49 | {-| -} 50 | small : Attr (R10.Context.ContextInternal z) decorative msg 51 | small = 52 | Font.size 14 53 | 54 | 55 | {-| -} 56 | xsmall : Attr (R10.Context.ContextInternal z) decorative msg 57 | xsmall = 58 | Font.size 13 59 | 60 | 61 | {-| -} 62 | xxsmall : Attr (R10.Context.ContextInternal z) decorative msg 63 | xxsmall = 64 | Font.size 12 65 | -------------------------------------------------------------------------------- /examples/pwa/review/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.1", 7 | "dependencies": { 8 | "direct": { 9 | "elm/core": "1.0.5", 10 | "elm/json": "1.1.3", 11 | "elm/project-metadata-utils": "1.0.1", 12 | "jfmengels/elm-review": "2.3.2", 13 | "jfmengels/elm-review-unused": "1.1.2", 14 | "jfmengels/review-common": "1.2.3", 15 | "leojpod/review-no-empty-html-text": "1.0.2", 16 | "stil4m/elm-syntax": "7.1.3" 17 | }, 18 | "indirect": { 19 | "elm/html": "1.0.0", 20 | "elm/parser": "1.1.0", 21 | "elm/random": "1.0.0", 22 | "elm/time": "1.0.0", 23 | "elm/url": "1.0.0", 24 | "elm/virtual-dom": "1.0.2", 25 | "elm-community/json-extra": "4.3.0", 26 | "elm-community/list-extra": "8.2.4", 27 | "elm-explorations/test": "1.2.2", 28 | "rtfeldman/elm-hex": "1.0.0", 29 | "rtfeldman/elm-iso8601-date-strings": "1.1.3", 30 | "stil4m/structured-writer": "1.0.3" 31 | } 32 | }, 33 | "test-dependencies": { 34 | "direct": { 35 | "elm-explorations/test": "1.2.2" 36 | }, 37 | "indirect": {} 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/R10/CountryCode.elm: -------------------------------------------------------------------------------- 1 | -- Eventually we could make this the same as R10.Country, but for the moment 2 | -- this is a much simpler version 3 | 4 | 5 | module R10.CountryCode exposing 6 | ( CountryCode(..) 7 | , decoder 8 | , default 9 | , fromString 10 | , isJapan 11 | , toString 12 | ) 13 | 14 | import Json.Decode.Exploration 15 | 16 | 17 | type CountryCode 18 | = JP 19 | | DE 20 | | Others String 21 | 22 | 23 | default : CountryCode 24 | default = 25 | Others "tw" 26 | 27 | 28 | decoder : Json.Decode.Exploration.Decoder CountryCode 29 | decoder = 30 | Json.Decode.Exploration.string 31 | |> Json.Decode.Exploration.andThen 32 | (\string -> Json.Decode.Exploration.succeed <| fromString string) 33 | 34 | 35 | fromString : String -> CountryCode 36 | fromString string = 37 | case String.toLower string of 38 | "de" -> 39 | DE 40 | 41 | "jp" -> 42 | JP 43 | 44 | _ -> 45 | Others string 46 | 47 | 48 | toString : CountryCode -> String 49 | toString countryCode = 50 | case countryCode of 51 | JP -> 52 | "jp" 53 | 54 | DE -> 55 | "de" 56 | 57 | Others string -> 58 | String.toLower string 59 | 60 | 61 | isJapan : CountryCode -> Bool 62 | isJapan countryCode = 63 | case countryCode of 64 | JP -> 65 | True 66 | 67 | _ -> 68 | False 69 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/ValidationCode.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.ValidationCode exposing (fromValidationCodeToMessageWithReplacedValues) 2 | 3 | import R10.Form.Internal.FieldConf 4 | import Regex 5 | 6 | 7 | fromValidationCodeToMessageWithReplacedValues : 8 | R10.Form.Internal.FieldConf.ValidationCode 9 | -> R10.Form.Internal.FieldConf.ValidationPayload 10 | -> (R10.Form.Internal.FieldConf.ValidationCode -> String) 11 | -> String 12 | fromValidationCodeToMessageWithReplacedValues validationCode bracketsArgs translator_ = 13 | let 14 | translated : String 15 | translated = 16 | translator_ validationCode 17 | in 18 | if List.isEmpty bracketsArgs then 19 | translated 20 | 21 | else 22 | replaceBrackets bracketsArgs translated 23 | 24 | 25 | replacer : ( Int, String ) -> String -> String 26 | replacer ( index, value ) acc = 27 | Regex.replace (regexBracket index) (\_ -> value) acc 28 | 29 | 30 | replaceBrackets : List String -> String -> String 31 | replaceBrackets values target = 32 | values 33 | |> List.indexedMap Tuple.pair 34 | |> List.foldl replacer target 35 | 36 | 37 | regexBracket : Int -> Regex.Regex 38 | regexBracket index = 39 | -- Removing all pairs of square brackets that just contain comments 40 | Regex.fromStringWith { caseInsensitive = True, multiline = True } ("\\{" ++ String.fromInt index ++ "\\}") 41 | |> Maybe.withDefault Regex.never 42 | -------------------------------------------------------------------------------- /examples/simpleForm/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "NoRedInk/elm-json-decode-pipeline": "1.0.0", 11 | "avh4/elm-color": "1.0.0", 12 | "elm/browser": "1.0.2", 13 | "elm/core": "1.0.5", 14 | "elm/html": "1.0.0", 15 | "elm/json": "1.1.3", 16 | "elm/parser": "1.1.0", 17 | "elm/regex": "1.0.0", 18 | "elm/svg": "1.0.1", 19 | "elm/time": "1.0.0", 20 | "elm/url": "1.0.0", 21 | "elm-community/json-extra": "4.3.0", 22 | "elm-community/list-extra": "8.5.2", 23 | "elm-community/string-extra": "4.0.1", 24 | "lucamug/elm-ui-with-context": "1.0.0", 25 | "mdgriffith/elm-ui": "1.1.8", 26 | "noahzgordon/elm-color-extra": "1.0.2", 27 | "rtfeldman/elm-iso8601-date-strings": "1.1.4", 28 | "tasuki/elm-punycode": "1.1.0", 29 | "zwilias/json-decode-exploration": "6.0.0" 30 | }, 31 | "indirect": { 32 | "elm/random": "1.0.0", 33 | "elm/virtual-dom": "1.0.2", 34 | "fredcy/elm-parseint": "2.0.1", 35 | "mgold/elm-nonempty-list": "4.1.0" 36 | } 37 | }, 38 | "test-dependencies": { 39 | "direct": {}, 40 | "indirect": {} 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/creditCardForm/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "NoRedInk/elm-json-decode-pipeline": "1.0.0", 11 | "avh4/elm-color": "1.0.0", 12 | "elm/browser": "1.0.2", 13 | "elm/core": "1.0.5", 14 | "elm/html": "1.0.0", 15 | "elm/json": "1.1.3", 16 | "elm/parser": "1.1.0", 17 | "elm/regex": "1.0.0", 18 | "elm/svg": "1.0.1", 19 | "elm/time": "1.0.0", 20 | "elm/url": "1.0.0", 21 | "elm-community/json-extra": "4.3.0", 22 | "elm-community/list-extra": "8.5.2", 23 | "elm-community/string-extra": "4.0.1", 24 | "lucamug/elm-ui-with-context": "1.0.0", 25 | "mdgriffith/elm-ui": "1.1.8", 26 | "noahzgordon/elm-color-extra": "1.0.2", 27 | "rtfeldman/elm-iso8601-date-strings": "1.1.4", 28 | "tasuki/elm-punycode": "1.1.0", 29 | "zwilias/json-decode-exploration": "6.0.0" 30 | }, 31 | "indirect": { 32 | "elm/random": "1.0.0", 33 | "elm/virtual-dom": "1.0.2", 34 | "fredcy/elm-parseint": "2.0.1", 35 | "mgold/elm-nonempty-list": "4.1.0" 36 | } 37 | }, 38 | "test-dependencies": { 39 | "direct": {}, 40 | "indirect": {} 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Html/String/Keyed.elm: -------------------------------------------------------------------------------- 1 | module Html.String.Keyed exposing 2 | ( node 3 | , ol, ul 4 | ) 5 | 6 | {-| A keyed node helps optimize cases where children are getting added, moved, 7 | removed, etc. Common examples include: 8 | 9 | - The user can delete items from a list. 10 | - The user can create new items in a list. 11 | - You can sort a list based on name or date or whatever. 12 | 13 | When you use a keyed node, every child is paired with a string identifier. This 14 | makes it possible for the underlying diffing algorithm to reuse nodes more 15 | efficiently. 16 | 17 | 18 | # Keyed Nodes 19 | 20 | @docs node 21 | 22 | 23 | # Commonly Keyed Nodes 24 | 25 | @docs ol, ul 26 | 27 | -} 28 | 29 | import Html.String exposing (Attribute, Html) 30 | import Html.Types 31 | 32 | 33 | {-| Works just like `Html.node`, but you add a unique identifier to each child 34 | node. You want this when you have a list of nodes that is changing: adding 35 | nodes, removing nodes, etc. In these cases, the unique identifiers help make 36 | the DOM modifications more efficient. 37 | -} 38 | node : String -> List (Attribute msg) -> List ( String, Html msg ) -> Html msg 39 | node tag attrs children = 40 | Html.Types.Node tag attrs (Html.Types.Keyed children) 41 | 42 | 43 | {-| -} 44 | ol : List (Attribute msg) -> List ( String, Html msg ) -> Html msg 45 | ol = 46 | node "ol" 47 | 48 | 49 | {-| -} 50 | ul : List (Attribute msg) -> List ( String, Html msg ) -> Html msg 51 | ul = 52 | node "ul" 53 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/ConfMeta.elm: -------------------------------------------------------------------------------- 1 | module Starter.ConfMeta exposing 2 | ( ConfMeta 3 | , confMeta 4 | ) 5 | 6 | import Starter.Version 7 | 8 | 9 | type alias ConfMeta = 10 | -- ports 11 | { portBuild : Int 12 | , portStatic : Int 13 | , portDev : Int 14 | 15 | -- 16 | , versionElmStarter : String 17 | 18 | -- 19 | , indentation : Int 20 | 21 | -- notifications 22 | , messageDoNotEditDisclaimer : String 23 | , messageEnableJavascriptForBetterExperience : String 24 | , messageLoading : String 25 | , messageYouNeedToEnableJavascript : String 26 | 27 | -- tags 28 | , tagLoader : String 29 | , tagNotification : String 30 | } 31 | 32 | 33 | confMeta : ConfMeta 34 | confMeta = 35 | -- ports 36 | { portStatic = 7000 37 | , portDev = 8000 38 | , portBuild = 9000 39 | 40 | -- 41 | , versionElmStarter = Starter.Version.version 42 | 43 | -- 44 | , indentation = 0 45 | 46 | -- messages 47 | , messageYouNeedToEnableJavascript = "You need to enable JavaScript to run this app." 48 | , messageEnableJavascriptForBetterExperience = "Enable Javascript for a better experience." 49 | , messageLoading = "L O A D I N G . . ." 50 | , messageDoNotEditDisclaimer = "Generated file ** DO NOT EDIT DIRECTLY ** Edit Elm files instead" 51 | 52 | -- tags 53 | , tagLoader = prefix ++ "notification" 54 | , tagNotification = prefix ++ "loader" 55 | } 56 | 57 | 58 | prefix : String 59 | prefix = 60 | "elm-starter-" 61 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/State.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.State exposing 2 | ( FiltersState(..) 3 | , FiltersStateRecord 4 | , PaginationButtonState(..) 5 | , PaginationState(..) 6 | , PaginationStateRecord 7 | , State 8 | ) 9 | 10 | import Dict 11 | import R10.Form 12 | 13 | 14 | type PaginationButtonState 15 | = PaginationButtonDisabled 16 | | PaginationButtonOtherLoading 17 | | PaginationButtonLoading 18 | | PaginationButtonEnabled 19 | 20 | 21 | type alias PaginationStateRecord = 22 | -- TODO there is too many combinations of nextButtonState + prevButtonState 23 | { length : Int 24 | , nextButtonState : PaginationButtonState 25 | , prevButtonState : PaginationButtonState 26 | 27 | -- todo replace button states with Edge+Loading states? Same number of combinations but likely clearer idea 28 | } 29 | 30 | 31 | type PaginationState 32 | = Pagination PaginationStateRecord 33 | | NoPagination 34 | 35 | 36 | type alias SortState = 37 | { name : String 38 | , isReversed : Bool 39 | } 40 | 41 | 42 | type alias FiltersStateRecord = 43 | { filterEditor : Maybe ( String, R10.Form.Form ) -- ( open on filter with key, popup inner form model) 44 | , filterValues : Dict.Dict String String 45 | } 46 | 47 | 48 | type FiltersState 49 | = Filters FiltersStateRecord 50 | | NoFilters 51 | 52 | 53 | type alias State = 54 | { sort : SortState 55 | , pagination : PaginationState 56 | , filters : FiltersState 57 | , loading : Bool 58 | } 59 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Types.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Types exposing (Filter(..), Sorter(..), Status(..)) 2 | 3 | {-| Specifies a particular way of sorting data. 4 | -} 5 | 6 | 7 | type Sorter data 8 | = None 9 | | Increasing (List data -> List data) 10 | | Decreasing (List data -> List data) 11 | | IncOrDec (List data -> List data) 12 | | DecOrInc (List data -> List data) 13 | 14 | 15 | {-| The status of a particular column, for use in the `thead` field of your 16 | `Customizations`. 17 | 18 | - If the column is unsortable, the status will always be `Unsortable`. 19 | - If the column can be sorted in one direction, the status will be `Sortable`. 20 | The associated boolean represents whether this column is selected. So it is 21 | `True` if the table is currently sorted by this column, and `False` otherwise. 22 | - If the column can be sorted in either direction, the status will be `Reversible`. 23 | The associated maybe tells you whether this column is selected. It is 24 | `Just isReversed` if the table is currently sorted by this column, and 25 | `Nothing` otherwise. The `isReversed` boolean lets you know which way it 26 | is sorted. 27 | 28 | This information lets you do custom header decorations for each scenario. 29 | 30 | -} 31 | type Status 32 | = Unsortable 33 | | Sortable Bool 34 | | Reversible (Maybe Bool) 35 | 36 | 37 | type Filter 38 | = FilterText { label : String, key : String } 39 | | FilterSelect { label : String, key : String, options : List { value : String, label : String } } 40 | -------------------------------------------------------------------------------- /examples/pwa/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "src-elm-starter", 6 | "../../src" 7 | ], 8 | "elm-version": "0.19.1", 9 | "dependencies": { 10 | "direct": { 11 | "NoRedInk/elm-json-decode-pipeline": "1.0.0", 12 | "avh4/elm-color": "1.0.0", 13 | "elm/browser": "1.0.2", 14 | "elm/core": "1.0.5", 15 | "elm/html": "1.0.0", 16 | "elm/http": "2.0.0", 17 | "elm/json": "1.1.3", 18 | "elm/regex": "1.0.0", 19 | "elm/svg": "1.0.1", 20 | "elm/time": "1.0.0", 21 | "elm/url": "1.0.0", 22 | "elm-community/json-extra": "4.3.0", 23 | "elm-community/list-extra": "8.2.4", 24 | "elm-community/string-extra": "4.0.1", 25 | "elm-explorations/markdown": "1.0.0", 26 | "mdgriffith/elm-ui": "1.1.8", 27 | "noahzgordon/elm-color-extra": "1.0.2", 28 | "zwilias/json-decode-exploration": "6.0.0" 29 | }, 30 | "indirect": { 31 | "elm/bytes": "1.0.8", 32 | "elm/file": "1.0.5", 33 | "elm/parser": "1.1.0", 34 | "elm/random": "1.0.0", 35 | "elm/virtual-dom": "1.0.2", 36 | "fredcy/elm-parseint": "2.0.1", 37 | "mgold/elm-nonempty-list": "4.1.0", 38 | "rtfeldman/elm-iso8601-date-strings": "1.1.3" 39 | } 40 | }, 41 | "test-dependencies": { 42 | "direct": {}, 43 | "indirect": {} 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/Manifest.elm: -------------------------------------------------------------------------------- 1 | module Starter.Manifest exposing 2 | ( Manifest 3 | , manifest 4 | ) 5 | 6 | import Json.Encode 7 | import Starter.Icon 8 | 9 | 10 | manifestIcon : String -> Int -> Json.Encode.Value 11 | manifestIcon relative size = 12 | let 13 | sizeString = 14 | String.fromInt size 15 | in 16 | Json.Encode.object 17 | [ ( "src", Json.Encode.string (Starter.Icon.iconFileName relative size) ) 18 | , ( "sizes", Json.Encode.string (sizeString ++ "x" ++ sizeString) ) 19 | , ( "type", Json.Encode.string "image/png" ) 20 | , ( "purpose", Json.Encode.string "any maskable" ) 21 | ] 22 | 23 | 24 | type alias Manifest = 25 | { iconSizes : List Int 26 | , themeColor : String 27 | , name : String 28 | , nameLong : String 29 | } 30 | 31 | 32 | manifest : String -> Manifest -> Json.Encode.Value 33 | manifest relative args = 34 | -- https://developer.mozilla.org/en-US/docs/Web/Manifest 35 | Json.Encode.object 36 | [ ( "short_name", Json.Encode.string args.name ) 37 | , ( "name", Json.Encode.string args.nameLong ) 38 | , ( "start_url", Json.Encode.string (relative ++ "/") ) 39 | , ( "display", Json.Encode.string "standalone" ) 40 | , ( "background_color", Json.Encode.string args.themeColor ) 41 | , ( "theme_color", Json.Encode.string args.themeColor ) 42 | , ( "icons", Json.Encode.list (manifestIcon relative) args.iconSizes ) 43 | 44 | -- TODO - add https://developer.mozilla.org/en-US/docs/Web/Manifest/screenshots 45 | ] 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present, Rakuten Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | * Neither the name of Rakuten Inc., nor the names of other 16 | contributors may be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/Utils/FocusOut.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.Utils.FocusOut exposing (onFocusOut) 2 | 3 | import Json.Decode as D 4 | 5 | 6 | outsideTarget : String -> String -> msg -> D.Decoder msg 7 | outsideTarget targetName dropdownId closeMsg = 8 | D.field targetName (isOutsideDropdown dropdownId) 9 | |> D.andThen 10 | (\isOutside -> 11 | if isOutside then 12 | D.succeed closeMsg 13 | 14 | else 15 | D.fail "inside dropdown" 16 | ) 17 | 18 | 19 | isOutsideDropdown : String -> D.Decoder Bool 20 | isOutsideDropdown dropdownId = 21 | D.oneOf 22 | [ D.field "id" D.string 23 | |> D.andThen 24 | (\id -> 25 | if dropdownId == id then 26 | -- found match by id 27 | D.succeed False 28 | 29 | else 30 | -- try next decoder 31 | D.fail "check parent node" 32 | ) 33 | , D.lazy 34 | (\_ -> 35 | isOutsideDropdown dropdownId |> D.field "parentNode" 36 | ) 37 | 38 | -- fallback if all previous decoders failed 39 | , D.succeed True 40 | ] 41 | 42 | 43 | onFocusOut : String -> msg -> D.Decoder msg 44 | onFocusOut containerId closeMsg = 45 | -- Every target that should be allowed (element contained in `containerId`), 46 | -- should be focusable. Otherwise relatedTarget would be null and it would be 47 | -- impossible to check if relatedTarget is a child of a target 48 | outsideTarget "relatedTarget" containerId closeMsg 49 | -------------------------------------------------------------------------------- /src/R10/Mode.elm: -------------------------------------------------------------------------------- 1 | module R10.Mode exposing (Mode(..), decodeJsonString, decoderExploration, default, fromString, isLight, toString, toggle) 2 | 3 | {-| Use `Mode` to determine if the application should be rendered with a `Light` or a `Dark` palette of colors. 4 | 5 | @docs Mode, decodeJsonString, decoderExploration, default, fromString, isLight, toString, toggle 6 | 7 | -} 8 | 9 | import Json.Decode 10 | import Json.Decode.Exploration 11 | 12 | 13 | {-| -} 14 | type Mode 15 | = Dark 16 | | Light 17 | 18 | 19 | {-| -} 20 | default : Mode 21 | default = 22 | Light 23 | 24 | 25 | {-| -} 26 | isLight : Mode -> Bool 27 | isLight mode = 28 | mode == Light 29 | 30 | 31 | {-| -} 32 | toggle : Mode -> Mode 33 | toggle mode = 34 | case mode of 35 | Dark -> 36 | Light 37 | 38 | Light -> 39 | Dark 40 | 41 | 42 | {-| -} 43 | fromString : String -> Mode 44 | fromString string = 45 | case String.toLower string of 46 | "dark" -> 47 | Dark 48 | 49 | _ -> 50 | default 51 | 52 | 53 | {-| -} 54 | toString : Mode -> String 55 | toString theme = 56 | case theme of 57 | Dark -> 58 | "Dark" 59 | 60 | Light -> 61 | "Light" 62 | 63 | 64 | {-| -} 65 | decoderExploration : Json.Decode.Exploration.Decoder Mode 66 | decoderExploration = 67 | Json.Decode.Exploration.string |> Json.Decode.Exploration.andThen (fromString >> Json.Decode.Exploration.succeed) 68 | 69 | 70 | decoder : Json.Decode.Decoder Mode 71 | decoder = 72 | Json.Decode.string |> Json.Decode.andThen (fromString >> Json.Decode.succeed) 73 | 74 | 75 | {-| -} 76 | decodeJsonString : Json.Decode.Value -> Result Json.Decode.Error Mode 77 | decodeJsonString value = 78 | Json.Decode.decodeValue decoder value 79 | -------------------------------------------------------------------------------- /examples/pwa/review/src/ReviewConfig.elm: -------------------------------------------------------------------------------- 1 | module ReviewConfig exposing (config) 2 | 3 | {-| Do not rename the ReviewConfig module or the config function, because 4 | `elm-review` will look for these. 5 | 6 | To add packages that contain rules, add them to this review project using 7 | 8 | `elm install author/packagename` 9 | 10 | when inside the directory containing this file. 11 | 12 | -} 13 | 14 | import NoEmptyText 15 | import NoExposingEverything 16 | import NoImportingEverything 17 | import NoMissingTypeAnnotation 18 | import NoMissingTypeAnnotationInLetIn 19 | import NoMissingTypeExpose 20 | import NoUnused.CustomTypeConstructorArgs 21 | import NoUnused.CustomTypeConstructors 22 | import NoUnused.Dependencies 23 | import NoUnused.Exports 24 | import NoUnused.Modules 25 | import NoUnused.Parameters 26 | import NoUnused.Patterns 27 | import NoUnused.Variables 28 | import Review.Rule exposing (Rule) 29 | 30 | 31 | config : List Rule 32 | config = 33 | [ NoEmptyText.rule 34 | , NoExposingEverything.rule 35 | , NoImportingEverything.rule [ "Element", "Test" ] 36 | , NoMissingTypeAnnotation.rule 37 | , NoMissingTypeAnnotationInLetIn.rule 38 | , NoMissingTypeExpose.rule 39 | , NoUnused.CustomTypeConstructors.rule [] 40 | , NoUnused.CustomTypeConstructorArgs.rule 41 | , NoUnused.Dependencies.rule 42 | , NoUnused.Exports.rule 43 | , NoUnused.Modules.rule 44 | , NoUnused.Parameters.rule 45 | , NoUnused.Patterns.rule 46 | , NoUnused.Variables.rule 47 | ] 48 | |> List.map 49 | (\rule -> 50 | rule 51 | |> Review.Rule.ignoreErrorsForDirectories 52 | [ "src-elm-starter" 53 | , "../../src" 54 | ] 55 | |> Review.Rule.ignoreErrorsForFiles 56 | [] 57 | ) 58 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/Key.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.Key exposing 2 | ( Key 3 | , KeyAsString 4 | , composeKey 5 | , composeMultiKeys 6 | , empty 7 | , fromList 8 | , fromString 9 | , headId 10 | , replaceLeaf 11 | , separator 12 | , toQuantity 13 | , toString 14 | ) 15 | 16 | 17 | type Key 18 | = Key (List String) 19 | 20 | 21 | type alias KeyAsString = 22 | String 23 | 24 | 25 | fromList : List String -> Key 26 | fromList list = 27 | Key (list |> List.filter (not << String.isEmpty)) 28 | 29 | 30 | empty : Key 31 | empty = 32 | fromList [] 33 | 34 | 35 | composeKey : Key -> String -> Key 36 | composeKey (Key keys) extraKey = 37 | if String.isEmpty extraKey then 38 | Key keys 39 | 40 | else 41 | Key (extraKey :: keys) 42 | 43 | 44 | composeMultiKeys : Key -> Int -> List Key 45 | composeMultiKeys key quantity = 46 | List.indexedMap 47 | (\index _ -> 48 | composeKey key (String.fromInt index) 49 | ) 50 | (List.repeat quantity ()) 51 | 52 | 53 | headId : Key -> String 54 | headId (Key list) = 55 | list 56 | |> List.head 57 | |> Maybe.withDefault "CANNOT_FIND_THE_HEAD_ID_IN_AN_EMPTY_KEY" 58 | 59 | 60 | separator : String 61 | separator = 62 | "/" 63 | 64 | 65 | toString : Key -> KeyAsString 66 | toString (Key keys) = 67 | String.join separator (List.reverse keys) 68 | 69 | 70 | fromString : KeyAsString -> Key 71 | fromString keyAsString = 72 | Key <| List.reverse <| String.split separator keyAsString 73 | 74 | 75 | toQuantity : Key -> Int 76 | toQuantity (Key keys) = 77 | List.length keys 78 | 79 | 80 | replaceLeaf : String -> Key -> Key 81 | replaceLeaf newLeaf (Key keyList) = 82 | if List.isEmpty keyList then 83 | fromList keyList 84 | 85 | else 86 | newLeaf :: List.drop 1 keyList |> fromList 87 | -------------------------------------------------------------------------------- /examples/pwa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.1", 3 | "name": "R10", 4 | "nameLong": "R10", 5 | "description": "Rakuten Elm front-end bootstrapper and components Library", 6 | "author": "Rakuten", 7 | "homepage": "https://r10.netlify.app/", 8 | "license": "© Rakuten, Inc. 2020 - present", 9 | "snapshotWidth": 700, 10 | "snapshotHeight": 350, 11 | "themeColor": { 12 | "red": 17, 13 | "green": 123, 14 | "blue": 180 15 | }, 16 | "devDependencies": { 17 | "chokidar-cli": "^2.1.0", 18 | "cloc": "^2.6.0", 19 | "concurrently": "^5.3.0", 20 | "elm": "^0.19.1-3", 21 | "elm-analyse": "^0.16.5", 22 | "elm-go": "^5.0.8", 23 | "html-minifier": "^4.0.0", 24 | "puppeteer": "^5.2.1", 25 | "terser": "^4.8.0" 26 | }, 27 | "scripts": { 28 | "start": "node ./src-elm-starter/elm-starter start", 29 | "build": "node ./src-elm-starter/elm-starter build", 30 | "serverBuild": "node ./src-elm-starter/elm-starter serverBuild", 31 | "publishRpaas": "cf push -f rpaas-manifest.yml -p ./elm-stuff/elm-starter-files/build/", 32 | "elm-starter:boot": "node ./src-elm-starter/elm-starter boot", 33 | "elm-starter:generateDevFiles": "node ./src-elm-starter/elm-starter generateDevFiles", 34 | "elm-starter:buildExpectingTheServerRunning": "node ./src-elm-starter/elm-starter buildExpectingTheServerRunning", 35 | "elm-starter:serverDev": "node ./src-elm-starter/elm-starter serverDev", 36 | "elm-starter:serverStatic": "node ./src-elm-starter/elm-starter serverStatic", 37 | "elm-starter:watchStartElm": "node ./src-elm-starter/elm-starter watchStartElm", 38 | "elm-starter:debug": "node ./src-elm-starter/elm-starter debug", 39 | "elm-starter:help": "node ./src-elm-starter/elm-starter --help", 40 | "elm-starter:version": "node ./src-elm-starter/elm-starter --version" 41 | }, 42 | "repository": { 43 | "type": "git", 44 | "url": "https://github.com/rakutentech/r10" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/R10/Transition.elm: -------------------------------------------------------------------------------- 1 | module R10.Transition exposing 2 | ( parseCharacteristics 3 | , transition 4 | ) 5 | 6 | import Element.WithContext exposing (..) 7 | import Html.Attributes 8 | import R10.Context 9 | import String.Extra 10 | 11 | 12 | 13 | -- Examples 14 | -- 15 | -- "color .2s ease-out, background-color .2s ease-out" 16 | -- "background-color 0.8s" 17 | -- "all 0.15s ease-out" 18 | -- "all 0.2s " 19 | -- "all 0.13s" 20 | -- "all 0.15s ease-in, opacity 0.15s 0.2s ease-in" 21 | -- "opacity 0.2s, visibility 0.2s" 22 | -- "color .2s ease-out, background-color .2s ease-out" 23 | -- "font-size 300ms 0ms ease-in, opacity 300ms 300ms ease-in" 24 | -- "0.2s" 25 | -- 26 | -- 27 | 28 | 29 | transition : String -> Attribute (R10.Context.ContextInternal z) msg 30 | transition characteristics = 31 | withContextAttribute <| \c -> htmlAttribute <| Html.Attributes.style "transition" (parseCharacteristics c.contextR10.debugger_transitionSpeed characteristics) 32 | 33 | 34 | parseCharacteristics : Float -> String -> String 35 | parseCharacteristics ratio characteristics = 36 | characteristics 37 | |> String.Extra.clean 38 | |> String.split "," 39 | |> List.map (parseUnit ratio) 40 | |> String.join "," 41 | 42 | 43 | parseUnit : Float -> String -> String 44 | parseUnit ratio unit = 45 | unit 46 | |> String.split " " 47 | |> List.map (parseSubUnit ratio) 48 | |> String.join " " 49 | 50 | 51 | parseSubUnit : Float -> String -> String 52 | parseSubUnit ratio subUnit = 53 | if String.endsWith "ms" subUnit then 54 | case String.toFloat ("0" ++ String.dropRight 2 subUnit) of 55 | Just value -> 56 | String.fromFloat (value * ratio) ++ "ms" 57 | 58 | Nothing -> 59 | subUnit 60 | 61 | else if String.endsWith "s" subUnit then 62 | case String.toFloat ("0" ++ String.dropRight 1 subUnit) of 63 | Just value -> 64 | String.fromFloat (value * ratio) ++ "s" 65 | 66 | Nothing -> 67 | subUnit 68 | 69 | else 70 | subUnit 71 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Style.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Style exposing 2 | ( defaultCellAttrs 3 | , defaultHeaderAttrs 4 | , defaultRowAttrs 5 | ) 6 | 7 | import Element.WithContext exposing (..) 8 | import Element.WithContext.Border as Border 9 | import Element.WithContext.Font as Font 10 | import Html.Attributes 11 | import R10.Context exposing (..) 12 | import R10.Transition 13 | 14 | 15 | 16 | -- Application specific 17 | 18 | 19 | borderColor : Color 20 | borderColor = 21 | Element.WithContext.rgba 0 0 0 0.2 22 | 23 | 24 | 25 | {- 26 | Following styles are from 27 | https://material.io/components/data-tables/#specs 28 | -} 29 | -- Table cell padding 30 | -- Corner radius 31 | -- Container outline 32 | -- Height and padding 33 | 34 | 35 | headerRowHeight : number 36 | headerRowHeight = 37 | 56 38 | 39 | 40 | columnPadding : number 41 | columnPadding = 42 | 16 43 | 44 | 45 | defaultHeaderAttrs : List (Attribute (R10.Context.ContextInternal z) msg) 46 | defaultHeaderAttrs = 47 | [ paddingXY columnPadding 0 48 | , spacing columnPadding 49 | , width (fill |> minimum 120) -- minimum 120: weird behaviour on shrink otherwise 50 | , height <| px headerRowHeight 51 | , htmlAttribute <| Html.Attributes.style "word-break" "break-all" 52 | , htmlAttribute (Html.Attributes.style "user-select" "none") 53 | , Font.extraBold 54 | , Border.color borderColor 55 | , Border.widthEach { bottom = 1, top = 0, left = 0, right = 0 } 56 | ] 57 | 58 | 59 | defaultRowAttrs : List (Attribute (R10.Context.ContextInternal z) msg) 60 | defaultRowAttrs = 61 | [ width fill 62 | , height fill 63 | , Border.color borderColor 64 | , Border.widthEach { bottom = 1, top = 0, left = 0, right = 0 } 65 | ] 66 | 67 | 68 | defaultCellAttrs : List (Attribute (R10.Context.ContextInternal z) msg) 69 | defaultCellAttrs = 70 | [ width (fill |> minimum 120) 71 | , R10.Transition.transition "all 0.25s ease-out" 72 | , height (fill |> minimum 54) 73 | , paddingXY 16 6 74 | , htmlAttribute <| Html.Attributes.style "word-break" "break-word" 75 | ] 76 | -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/TextColors.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.TextColors exposing 2 | ( getBorderColor 3 | , getLabelColor 4 | ) 5 | 6 | import Element.WithContext exposing (..) 7 | import R10.FormComponents.Internal.UI.Color 8 | import R10.Palette 9 | 10 | 11 | getBorderColor : 12 | { a 13 | | focused : Bool 14 | , disabled : Bool 15 | , maybeValid : Maybe Bool 16 | , displayValidation : Bool 17 | , isMouseOver : Bool 18 | , palette : R10.Palette.Palette 19 | } 20 | -> Color 21 | getBorderColor { disabled, focused, maybeValid, displayValidation, isMouseOver, palette } = 22 | let 23 | validationActive : Bool 24 | validationActive = 25 | displayValidation && maybeValid == Just False 26 | 27 | alpha : Float 28 | alpha = 29 | if not disabled && (focused || isMouseOver || validationActive) then 30 | 1 31 | 32 | else 33 | 0.7 34 | in 35 | case ( displayValidation, maybeValid, focused ) of 36 | ( True, Just True, _ ) -> 37 | -- VALID 38 | R10.FormComponents.Internal.UI.Color.successA alpha palette 39 | 40 | ( True, Just False, _ ) -> 41 | -- NOT VALID 42 | R10.FormComponents.Internal.UI.Color.errorA alpha palette 43 | 44 | ( _, _, _ ) -> 45 | -- NORMAL 46 | R10.FormComponents.Internal.UI.Color.borderA alpha palette 47 | 48 | 49 | getLabelColor : 50 | { a 51 | | maybeValid : Maybe Bool 52 | , displayValidation : Bool 53 | , palette : R10.Palette.Palette 54 | } 55 | -> Color 56 | getLabelColor { maybeValid, displayValidation, palette } = 57 | case ( displayValidation, maybeValid ) of 58 | ( True, Just True ) -> 59 | -- VALID 60 | R10.FormComponents.Internal.UI.Color.success palette 61 | 62 | ( True, Just False ) -> 63 | -- NOT VALID 64 | R10.FormComponents.Internal.UI.Color.error palette 65 | 66 | ( _, _ ) -> 67 | -- NORMAL 68 | R10.FormComponents.Internal.UI.Color.label palette 69 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Html/String/Extra.elm: -------------------------------------------------------------------------------- 1 | module Html.String.Extra exposing 2 | ( body 3 | , charset 4 | , content 5 | , crossorigin 6 | , doctype 7 | , head 8 | , html 9 | , httpEquiv 10 | , link 11 | , meta 12 | , noscript 13 | , property_ 14 | , script 15 | , style_ 16 | , title_ 17 | ) 18 | 19 | import Html.String exposing (..) 20 | import Html.String.Attributes exposing (..) 21 | 22 | 23 | 24 | -- HTML EXTENSIONS 25 | -- 26 | -- Adding nodes and attributes useful to generate an entire HTML page 27 | 28 | 29 | doctype : String 30 | doctype = 31 | "" 32 | 33 | 34 | html : List (Attribute msg) -> List (Html msg) -> Html msg 35 | html = 36 | node "html" 37 | 38 | 39 | head : List (Attribute msg) -> List (Html msg) -> Html msg 40 | head = 41 | node "head" 42 | 43 | 44 | body : List (Attribute msg) -> List (Html msg) -> Html msg 45 | body = 46 | node "body" 47 | 48 | 49 | script : List (Attribute msg) -> List (Html msg) -> Html msg 50 | script = 51 | node "script" 52 | 53 | 54 | noscript : List (Attribute msg) -> List (Html msg) -> Html msg 55 | noscript = 56 | node "noscript" 57 | 58 | 59 | meta : List (Attribute msg) -> List (Html msg) -> Html msg 60 | meta = 61 | node "meta" 62 | 63 | 64 | link : List (Attribute msg) -> List (Html msg) -> Html msg 65 | link = 66 | node "link" 67 | 68 | 69 | style_ : List (Attribute msg) -> List (Html msg) -> Html msg 70 | style_ = 71 | node "style" 72 | 73 | 74 | title_ : List (Attribute msg) -> List (Html msg) -> Html msg 75 | title_ = 76 | node "title" 77 | 78 | 79 | charset : String -> Attribute msg 80 | charset = 81 | attribute "charset" 82 | 83 | 84 | crossorigin : String -> Attribute msg 85 | crossorigin = 86 | attribute "crossorigin" 87 | 88 | 89 | property_ : String -> Attribute msg 90 | property_ = 91 | attribute "property" 92 | 93 | 94 | content : String -> Attribute msg 95 | content = 96 | attribute "content" 97 | 98 | 99 | httpEquiv : String -> Attribute msg 100 | httpEquiv = 101 | attribute "http-equiv" 102 | -------------------------------------------------------------------------------- /src/R10/Card.elm: -------------------------------------------------------------------------------- 1 | module R10.Card exposing (high, low, normal, noShadow) 2 | 3 | {-| Cards are area of the page surranded by a rounded border. 4 | 5 | @docs high, low, normal, noShadow 6 | 7 | -} 8 | 9 | import Element.WithContext exposing (..) 10 | import Element.WithContext.Border as Border 11 | import R10.Color.AttrsBackground 12 | import R10.Color.AttrsBorder 13 | import R10.Color.AttrsFont 14 | import R10.Context exposing (..) 15 | import R10.Mode 16 | import R10.Transition 17 | 18 | 19 | shadow : Float -> Attr (R10.Context.ContextInternal z) decorative msg 20 | shadow level = 21 | Border.shadow 22 | { offset = ( 0, level ) 23 | , blur = level + 2 24 | , size = level - 2 25 | , color = rgba255 0 0 0 0.07 26 | } 27 | 28 | 29 | base : List (Attribute (R10.Context.ContextInternal context) msg) 30 | base = 31 | [ R10.Color.AttrsBackground.surface 32 | , R10.Color.AttrsBorder.normal 33 | , R10.Color.AttrsFont.normal 34 | , Border.rounded 10 35 | , Border.width 1 36 | , withContextAttribute <| 37 | \c -> 38 | Border.color <| 39 | case c.contextR10.theme.mode of 40 | R10.Mode.Light -> 41 | rgba 0 0 0 0.1 42 | 43 | R10.Mode.Dark -> 44 | rgba 1 1 1 0.08 45 | , padding 30 46 | , width fill 47 | , height fill 48 | , R10.Transition.transition "background-color 0.8s" 49 | ] 50 | 51 | 52 | {-| Card with an high shadow 53 | -} 54 | high : List (Attribute (R10.Context.ContextInternal context) msg) 55 | high = 56 | -- https://material.io/design/environment/elevation.html#elevation-in-material-design 57 | base ++ [ shadow 8 ] 58 | 59 | 60 | {-| Card with a normal shadow 61 | -} 62 | normal : List (Attribute (R10.Context.ContextInternal context) msg) 63 | normal = 64 | -- base ++ [ shadow 2, mouseOver [ shadow 8 ] ] 65 | base ++ [ shadow 4 ] 66 | 67 | 68 | {-| Card with a low shadow 69 | -} 70 | low : List (Attribute (R10.Context.ContextInternal context) msg) 71 | low = 72 | base ++ [ shadow 2 ] 73 | 74 | 75 | {-| Card without a shadow 76 | -} 77 | noShadow : List (Attribute (R10.Context.ContextInternal context) msg) 78 | noShadow = 79 | base 80 | -------------------------------------------------------------------------------- /src/R10/Footer.elm: -------------------------------------------------------------------------------- 1 | module R10.Footer exposing (view) 2 | 3 | {-| Generic footer. 4 | 5 | Be aware that this component depends on the `Header` component for historical and logical reasons. 6 | 7 | @docs view 8 | 9 | -} 10 | 11 | import Element.WithContext exposing (..) 12 | import R10.Context exposing (..) 13 | import Element.WithContext.Background as Background 14 | import Element.WithContext.Font as Font 15 | import R10.FontSize 16 | import R10.Header 17 | 18 | 19 | {-| -} 20 | view : R10.Header.Header -> R10.Header.ViewArgs z msg route -> Element (R10.Context.ContextInternal z) msg 21 | view model args = 22 | column 23 | [ width fill 24 | , Background.color <| rgb 0.3 0.3 0.3 25 | , Font.color <| rgb 0.8 0.8 0.8 26 | , paddingXY 0 60 27 | , R10.FontSize.normal 28 | , spacing 50 29 | ] 30 | <| 31 | [ el 32 | [ moveRight 14 33 | , width (fill |> maximum model.maxWidth) 34 | , centerX 35 | ] 36 | <| 37 | args.logoOnDark 38 | , row 39 | [ width (fill |> maximum model.maxWidth) 40 | , centerX 41 | ] 42 | [ column [ width fill, alignTop ] 43 | args.extraContent 44 | , column [ width fill, alignTop ] <| 45 | [] 46 | ++ R10.Header.languageMenu model args 47 | ++ R10.Header.menuSeparator 48 | ++ [ case model.session of 49 | R10.Header.SessionNotRequired -> 50 | none 51 | 52 | R10.Header.SessionNotRequested -> 53 | none 54 | 55 | R10.Header.SessionFetching -> 56 | none 57 | 58 | R10.Header.SessionSuccess _ -> 59 | map args.msgMapper <| R10.Header.logoutLink model args 60 | 61 | R10.Header.SessionError _ -> 62 | map args.msgMapper <| R10.Header.urlLogin model args 63 | ] 64 | ] 65 | , el [ alignBottom, centerX, Font.size 14 ] <| text "© Rakuten, Inc." 66 | ] 67 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Html/String/Lazy.elm: -------------------------------------------------------------------------------- 1 | module Html.String.Lazy exposing (lazy, lazy2, lazy3, lazy4, lazy5, lazy6, lazy7, lazy8) 2 | 3 | {-| 4 | 5 | 6 | # 🔥 This isn't actually lazy in this library.. 7 | 8 | .. because we can't keep track of the model without existential types. It just 9 | eagerly evaluates. This set of function is here to serve as a drop-in 10 | replacement. 11 | 12 | @docs lazy, lazy2, lazy3, lazy4, lazy5, lazy6, lazy7, lazy8 13 | 14 | -} 15 | 16 | import Html.String exposing (Html) 17 | 18 | 19 | {-| A performance optimization that delays the building of virtual DOM nodes. 20 | 21 | Calling `(view model)` will definitely build some virtual DOM, perhaps a lot of 22 | it. Calling `(lazy view model)` delays the call until later. During diffing, we 23 | can check to see if `model` is referentially equal to the previous value used, 24 | and if so, we just stop. No need to build up the tree structure and diff it, 25 | we know if the input to `view` is the same, the output must be the same! 26 | 27 | -} 28 | lazy : (a -> Html msg) -> a -> Html msg 29 | lazy f x = 30 | f x 31 | 32 | 33 | {-| Same as `lazy` but checks on two arguments. 34 | -} 35 | lazy2 : (a -> b -> Html msg) -> a -> b -> Html msg 36 | lazy2 f x y = 37 | f x y 38 | 39 | 40 | {-| Same as `lazy` but checks on three arguments. 41 | -} 42 | lazy3 : (a -> b -> c -> Html msg) -> a -> b -> c -> Html msg 43 | lazy3 f x y z = 44 | f x y z 45 | 46 | 47 | {-| Same as `lazy` but checks on four arguments. 48 | -} 49 | lazy4 : (a -> b -> c -> Html msg) -> a -> b -> c -> Html msg 50 | lazy4 f x y z = 51 | f x y z 52 | 53 | 54 | {-| Same as `lazy` but checks on five arguments. 55 | -} 56 | lazy5 : (a -> b -> c -> Html msg) -> a -> b -> c -> Html msg 57 | lazy5 f x y z = 58 | f x y z 59 | 60 | 61 | {-| Same as `lazy` but checks on six arguments. 62 | -} 63 | lazy6 : (a -> b -> c -> Html msg) -> a -> b -> c -> Html msg 64 | lazy6 f x y z = 65 | f x y z 66 | 67 | 68 | {-| Same as `lazy` but checks on seven arguments. 69 | -} 70 | lazy7 : (a -> b -> c -> Html msg) -> a -> b -> c -> Html msg 71 | lazy7 f x y z = 72 | f x y z 73 | 74 | 75 | {-| Same as `lazy` but checks on eight arguments. 76 | -} 77 | lazy8 : (a -> b -> c -> Html msg) -> a -> b -> c -> Html msg 78 | lazy8 f x y z = 79 | f x y z 80 | -------------------------------------------------------------------------------- /src/R10/Translations.elm: -------------------------------------------------------------------------------- 1 | module R10.Translations exposing (language, signIn, signInHeader, signOut) 2 | 3 | {-| Text translations. 4 | 5 | This is an examples of translation: 6 | 7 | signInHeader : R10.Language.Translations 8 | signInHeader = 9 | { key = "signInHeader" 10 | , en_us = "Sign in to your Rakuten account" 11 | , ja_jp = "楽天会員 ログイン" 12 | , zh_tw = "登錄您的Rakuten帳戶" 13 | , de_de = "Loggen Sie sich in Ihr Rakuten-Konto ein" 14 | , fr_fr = "Connectez-vous à votre compte Rakuten" 15 | } 16 | 17 | @docs language, signIn, signInHeader, signOut 18 | 19 | -} 20 | 21 | import R10.Language 22 | 23 | 24 | {-| -} 25 | signInHeader : R10.Language.Translations 26 | signInHeader = 27 | { key = "signInHeader" 28 | , en_us = "Sign in to your Rakuten account" 29 | , ja_jp = "楽天会員 ログイン" 30 | , zh_tw = "登錄您的Rakuten帳戶" 31 | , zh_cn = "登录您的Rakuten帐户" 32 | , de_de = "Loggen Sie sich in Ihr Rakuten-Konto ein[[R]]" 33 | , fr_fr = "Connectez-vous à votre compte Rakuten" 34 | , es_es = "Inicie sesión en su cuenta de Rakuten" 35 | , it_it = "Collegati col tuo account Rakuten" 36 | , uk_ua = "" 37 | , pt_pt = "" 38 | , nl_nl = "" 39 | } 40 | 41 | 42 | {-| -} 43 | language : R10.Language.Translations 44 | language = 45 | { key = "language" 46 | , en_us = "Language" 47 | , ja_jp = "言語" 48 | , zh_tw = "語言" 49 | , zh_cn = "语言" 50 | , de_de = "Sprache" 51 | , fr_fr = "Langue" 52 | , es_es = "Idioma" 53 | , it_it = "Lingua" 54 | , uk_ua = "" 55 | , pt_pt = "" 56 | , nl_nl = "" 57 | } 58 | 59 | 60 | {-| -} 61 | signOut : R10.Language.Translations 62 | signOut = 63 | { key = "signOut" 64 | , en_us = "Sign Out" 65 | , ja_jp = "ログアウト" 66 | , zh_tw = "登出" 67 | , zh_cn = "登出" 68 | , de_de = "Ausloggen" 69 | , fr_fr = "Déconnexion" 70 | , es_es = "Desconectar" 71 | , it_it = "Disconnetti" 72 | , uk_ua = "" 73 | , pt_pt = "" 74 | , nl_nl = "" 75 | } 76 | 77 | 78 | {-| -} 79 | signIn : R10.Language.Translations 80 | signIn = 81 | { key = "signIn" 82 | , en_us = "Sign in" 83 | , ja_jp = "ログイン" 84 | , zh_tw = "登入" 85 | , zh_cn = "登入" 86 | , de_de = "Anmelden" 87 | , fr_fr = "Se connecter" 88 | , es_es = "Registrarse" 89 | , it_it = "" 90 | , uk_ua = "" 91 | , pt_pt = "" 92 | , nl_nl = "" 93 | } 94 | -------------------------------------------------------------------------------- /src/R10/Libu.elm: -------------------------------------------------------------------------------- 1 | module R10.Libu exposing (view, Type(..)) 2 | 3 | {-| 4 | 5 | ██ ██ ██████ ██ ██ 6 | ██ ██ ██ ██ ██ ██ 7 | ██ ██ ██████ ██ ██ 8 | ██ ██ ██ ██ ██ ██ 9 | ███████ ██ ██████ ██████ 10 | 11 | Hi! I'm Libu, your new friend, half Link and half Button 12 | 13 | I am a super-entity that will take care of all your hypertext necessity 14 | as I will let user to go wherever they want. 15 | 16 | @docs view, Type 17 | 18 | -} 19 | 20 | import Element.WithContext exposing (..) 21 | import Element.WithContext.Input as Input 22 | import Html 23 | import Html.Events 24 | import Json.Decode 25 | import R10.Context exposing (..) 26 | 27 | 28 | {-| `LiNewTab` => A link that open in a new tab. 29 | 30 | `LiInternal` => A link that prevent the default behavior of the browser, to be used in SPA so that the browser doesn't refresh. 31 | 32 | `Li` => Just a link. 33 | 34 | `Bu` => Just a button. 35 | 36 | -} 37 | type Type msg 38 | = LiNewTab String 39 | | LiInternal String (String -> msg) 40 | | Li String 41 | | Bu (Maybe msg) 42 | 43 | 44 | {-| -} 45 | view : 46 | List (Attribute (R10.Context.ContextInternal z) msg) 47 | -> { type_ : Type msg, label : Element (R10.Context.ContextInternal z) msg } 48 | -> Element (R10.Context.ContextInternal z) msg 49 | view attrs args = 50 | case args.type_ of 51 | Li url -> 52 | link 53 | attrs 54 | { label = args.label 55 | , url = url 56 | } 57 | 58 | LiNewTab url -> 59 | newTabLink 60 | attrs 61 | { label = args.label 62 | , url = url 63 | } 64 | 65 | LiInternal url onClick -> 66 | let 67 | -- From https://github.com/elm/browser/blob/1.0.2/notes/navigation-in-elements.md 68 | preventDefault : msg -> Html.Attribute msg 69 | preventDefault msg = 70 | Html.Events.preventDefaultOn "click" (Json.Decode.succeed ( msg, True )) 71 | in 72 | link 73 | ((htmlAttribute <| preventDefault (onClick url)) :: attrs) 74 | { label = args.label 75 | , url = url 76 | } 77 | 78 | Bu onPress -> 79 | Input.button 80 | attrs 81 | { label = args.label 82 | , onPress = onPress 83 | } 84 | -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/ExtraCss.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.ExtraCss exposing (extraCss) 2 | 3 | import Element.WithContext exposing (..) 4 | import R10.Context exposing (..) 5 | import R10.FormComponents.Internal.Single 6 | import R10.FormComponents.Internal.Text 7 | import R10.FormComponents.Internal.UI.Color 8 | import R10.FormComponents.Internal.Validations 9 | import R10.Palette 10 | 11 | 12 | extraCss : R10.Palette.Palette -> String 13 | extraCss palette = 14 | R10.FormComponents.Internal.Text.extraCss 15 | ++ R10.FormComponents.Internal.Single.extraCss 16 | ++ R10.FormComponents.Internal.Validations.extraCss 17 | ++ ripple 18 | (R10.FormComponents.Internal.UI.Color.onSurface palette) 19 | (R10.FormComponents.Internal.UI.Color.primary palette) 20 | (R10.FormComponents.Internal.UI.Color.onPrimary palette) 21 | 22 | 23 | ripple : Color -> Color -> Color -> String 24 | ripple onSurface primary onPrimary = 25 | let 26 | onSurfaceStr : String 27 | onSurfaceStr = 28 | onSurface |> R10.FormComponents.Internal.UI.Color.toCssString 29 | 30 | primaryStr : String 31 | primaryStr = 32 | primary |> R10.FormComponents.Internal.UI.Color.toCssString 33 | 34 | onPrimaryStr : String 35 | onPrimaryStr = 36 | onPrimary |> R10.FormComponents.Internal.UI.Color.toCssString 37 | in 38 | rippleStr_ "ripple" onSurfaceStr 39 | ++ rippleStr_ "ripple-primary" primaryStr 40 | ++ rippleStr_ "ripple-on-primary" onPrimaryStr 41 | 42 | 43 | rippleStr_ : String -> String -> String 44 | rippleStr_ className colorStr = 45 | """ 46 | .""" ++ className ++ """ { 47 | position: relative; 48 | overflow: hidden; 49 | transform: translate3d(0, 0, 0); 50 | } 51 | .""" ++ className ++ """:after { 52 | content: ""; 53 | display: block; 54 | position: absolute; 55 | width: 100%; 56 | height: 100%; 57 | top: 0; 58 | left: 0; 59 | pointer-events: none; 60 | background-image: radial-gradient(circle, """ ++ colorStr ++ """ 10%, transparent 10.01%); 61 | background-repeat: no-repeat; 62 | background-position: 50%; 63 | transform: scale(10, 10); 64 | opacity: 0; 65 | transition: transform .5s, opacity 1s; 66 | } 67 | .""" ++ className ++ """:active:after { 68 | transform: scale(0, 0); 69 | opacity: .2; 70 | transition: 0s; 71 | } 72 | """ 73 | -------------------------------------------------------------------------------- /examples/simpleView/src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | import Element.WithContext exposing (..) 4 | import Element.WithContext.Background as Background 5 | import Element.WithContext.Border as Border 6 | import Element.WithContext.Font as Font 7 | import Html 8 | import R10.Button 9 | import R10.Card 10 | import R10.Color 11 | import R10.Color.AttrsBackground 12 | import R10.Color.Svg 13 | import R10.Context 14 | import R10.Device 15 | import R10.FontSize 16 | import R10.Language 17 | import R10.Libu 18 | import R10.Mode 19 | import R10.Paragraph 20 | import R10.Svg.Icons 21 | import R10.Svg.Logos 22 | import R10.Svg.LogosExtra 23 | import R10.Theme 24 | 25 | 26 | main : Html.Html msg 27 | main = 28 | layout R10.Context.default [ R10.Color.AttrsBackground.background, padding 20, R10.FontSize.normal ] <| 29 | column 30 | (R10.Card.high 31 | ++ [ centerX 32 | , centerY 33 | , width (fill |> maximum 360) 34 | , height shrink 35 | , spacing 30 36 | ] 37 | ) 38 | [ withContext <| \c -> R10.Svg.LogosExtra.r10 [ centerX ] (R10.Color.Svg.logo c.contextR10.theme) 32 39 | , R10.Paragraph.normalMarkdown [ Font.center ] "This is an example of a view made with [Elm](https://elm-lang.org/), [elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/) and [R10](https://package.elm-lang.org/packages/rakutentech/r10/latest/) ([Source code](https://github.com/rakutentech/r10/blob/master/examples/simpleView/src/Main.elm))." 40 | , el [ Font.size 60, centerX, padding 10 ] <| text "🎉" 41 | , R10.Button.primary [] 42 | { label = 43 | withContext <| 44 | \c -> 45 | row [ spacing 15, centerX ] 46 | [ R10.Paragraph.normal [] [ text "Primary Buttons" ] 47 | , R10.Svg.Icons.cart_f [] (R10.Color.Svg.fontHighEmphasisWithMaximumContrast c.contextR10.theme) 18 48 | ] 49 | , libu = R10.Libu.Li "https://r10.netlify.app" 50 | , translation = { key = "primary-example" } 51 | } 52 | , R10.Button.secondary [] 53 | { label = R10.Paragraph.normal [] [ text "Secondary Buttons" ] 54 | , libu = R10.Libu.Li "https://r10.netlify.app" 55 | , translation = { key = "secodnary-example" } 56 | } 57 | ] 58 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Svg.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Svg exposing 2 | ( arrowDown 3 | , arrowNext 4 | , arrowPrev 5 | , filterList 6 | , removeCircle 7 | ) 8 | 9 | import Svg 10 | import Svg.Attributes as SA 11 | 12 | 13 | wrapperWithViewbox : Int -> String -> List (Svg.Svg msg) -> Svg.Svg msg 14 | wrapperWithViewbox size viewbox listSvg = 15 | Svg.svg 16 | [ SA.xmlSpace "http://www.w3.org/2000/svg" 17 | , SA.viewBox viewbox 18 | , SA.height <| String.fromInt size 19 | , SA.preserveAspectRatio "xMinYMin slice" 20 | ] 21 | listSvg 22 | 23 | 24 | arrowPrev : String -> Int -> Svg.Svg msg 25 | arrowPrev color size = 26 | wrapperWithViewbox 27 | size 28 | "0 0 24 24" 29 | [ Svg.path [ SA.fill color, SA.d "M16.62 2.99c-.49-.49-1.28-.49-1.77 0L6.54 11.3c-.39.39-.39 1.02 0 1.41l8.31 8.31c.49.49 1.28.49 1.77 0s.49-1.28 0-1.77L9.38 12l7.25-7.25c.48-.48.48-1.28-.01-1.76z" ] [] 30 | ] 31 | 32 | 33 | arrowNext : String -> Int -> Svg.Svg msg 34 | arrowNext color size = 35 | wrapperWithViewbox 36 | size 37 | "0 0 24 24" 38 | [ Svg.path [ SA.fill color, SA.d "M7.38 21.01c.49.49 1.28.49 1.77 0l8.31-8.31c.39-.39.39-1.02 0-1.41L9.15 2.98c-.49-.49-1.28-.49-1.77 0s-.49 1.28 0 1.77L14.62 12l-7.25 7.25c-.48.48-.48 1.28.01 1.76z" ] [] 39 | ] 40 | 41 | 42 | arrowDown : String -> Int -> Svg.Svg msg 43 | arrowDown color size = 44 | wrapperWithViewbox 45 | size 46 | "0 0 24 24" 47 | [ Svg.path [ SA.fill color, SA.d "M11 5v11.17l-4.88-4.88c-.39-.39-1.03-.39-1.42 0-.39.39-.39 1.02 0 1.41l6.59 6.59c.39.39 1.02.39 1.41 0l6.59-6.59c.39-.39.39-1.02 0-1.41-.39-.39-1.02-.39-1.41 0L13 16.17V5c0-.55-.45-1-1-1s-1 .45-1 1z" ] [] 48 | ] 49 | 50 | 51 | removeCircle : String -> Int -> Svg.Svg msg 52 | removeCircle color size = 53 | wrapperWithViewbox 54 | size 55 | "0 0 24 24" 56 | [ Svg.path [ SA.fill color, SA.d "M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z" ] [] 57 | ] 58 | 59 | 60 | filterList : String -> Int -> Svg.Svg msg 61 | filterList color size = 62 | wrapperWithViewbox 63 | size 64 | "0 0 24 24" 65 | [ Svg.path [ SA.fill color, SA.d "M11 18h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM3 7c0 .55.45 1 1 1h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1zm4 6h10c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1z" ] [] 66 | ] 67 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "rakutentech/r10", 4 | "summary": "Bootstrapper and UI Components we use at Rakuten", 5 | "license": "BSD-3-Clause", 6 | "version": "7.0.0", 7 | "exposed-modules": [ 8 | "R10.Button", 9 | "R10.Card", 10 | "R10.Color", 11 | "R10.Context", 12 | "R10.Counter", 13 | "R10.Country", 14 | "R10.Device", 15 | "R10.DropDown", 16 | "R10.FontSize", 17 | "R10.Footer", 18 | "R10.Form", 19 | "R10.FormDebug", 20 | "R10.FormTypes", 21 | "R10.Header", 22 | "R10.I18n", 23 | "R10.Language", 24 | "R10.LanguageSelector", 25 | "R10.Libu", 26 | "R10.Link", 27 | "R10.Mode", 28 | "R10.Okaimonopanda", 29 | "R10.Paragraph", 30 | "R10.SimpleMarkdown", 31 | "R10.Table", 32 | "R10.Theme", 33 | "R10.Translations", 34 | "R10.Color.AttrsBackground", 35 | "R10.Color.AttrsBorder", 36 | "R10.Color.AttrsFont", 37 | "R10.Color.Svg", 38 | "R10.Color.Utils", 39 | "R10.Svg.Icons", 40 | "R10.Svg.IconsExtra", 41 | "R10.Svg.Lists", 42 | "R10.Svg.Logos", 43 | "R10.Svg.LogosExtra", 44 | "R10.Svg.Others", 45 | "R10.Svg.Utils" 46 | ], 47 | "elm-version": "0.19.0 <= v < 0.20.0", 48 | "dependencies": { 49 | "NoRedInk/elm-json-decode-pipeline": "1.0.0 <= v < 2.0.0", 50 | "avh4/elm-color": "1.0.0 <= v < 2.0.0", 51 | "elm/browser": "1.0.2 <= v < 2.0.0", 52 | "elm/core": "1.0.2 <= v < 2.0.0", 53 | "elm/html": "1.0.0 <= v < 2.0.0", 54 | "elm/http": "2.0.0 <= v < 3.0.0", 55 | "elm/json": "1.1.3 <= v < 2.0.0", 56 | "elm/parser": "1.1.0 <= v < 2.0.0", 57 | "elm/regex": "1.0.0 <= v < 2.0.0", 58 | "elm/svg": "1.0.1 <= v < 2.0.0", 59 | "elm/time": "1.0.0 <= v < 2.0.0", 60 | "elm/url": "1.0.0 <= v < 2.0.0", 61 | "elm-community/json-extra": "4.3.0 <= v < 5.0.0", 62 | "elm-community/list-extra": "8.2.4 <= v < 9.0.0", 63 | "elm-community/string-extra": "4.0.1 <= v < 5.0.0", 64 | "lucamug/elm-ui-with-context": "1.0.0 <= v < 2.0.0", 65 | "mdgriffith/elm-ui": "1.1.7 <= v < 2.0.0", 66 | "noahzgordon/elm-color-extra": "1.0.2 <= v < 2.0.0", 67 | "rtfeldman/elm-iso8601-date-strings": "1.1.4 <= v < 2.0.0", 68 | "tasuki/elm-punycode": "1.1.0 <= v < 2.0.0", 69 | "zwilias/json-decode-exploration": "6.0.0 <= v < 7.0.0" 70 | }, 71 | "test-dependencies": {} 72 | } -------------------------------------------------------------------------------- /examples/pwa/src/Pages/Counter.elm: -------------------------------------------------------------------------------- 1 | module Pages.Counter exposing 2 | ( Model 3 | , Msg 4 | , init 5 | , subscriptions 6 | , update 7 | , view 8 | ) 9 | 10 | import Browser.Events 11 | import Element exposing (..) 12 | import Element.Font as Font 13 | import R10.Button 14 | import R10.Counter 15 | import R10.Libu 16 | import R10.Theme 17 | 18 | 19 | type alias Model = 20 | { counter : R10.Counter.Counter } 21 | 22 | 23 | init : Model 24 | init = 25 | { counter = 26 | R10.Counter.init 27 | |> R10.Counter.start 28 | } 29 | 30 | 31 | type Msg 32 | = OnAnimationFrame 33 | | GoTo Int 34 | | Add Int 35 | 36 | 37 | update : Msg -> Model -> Model 38 | update msg model = 39 | case msg of 40 | OnAnimationFrame -> 41 | { model | counter = R10.Counter.update model.counter } 42 | 43 | GoTo value -> 44 | { model | counter = R10.Counter.moveTo value model.counter } 45 | 46 | Add value -> 47 | { model | counter = R10.Counter.add value model.counter } 48 | 49 | 50 | view : Model -> R10.Theme.Theme -> List (Element Msg) 51 | view model theme = 52 | [ el [ Font.family [ Font.monospace ] ] <| R10.Counter.view model.counter 100 53 | , row [ spacing 20 ] 54 | [ R10.Button.secondary [ width shrink ] 55 | { label = text "Jump To 10" 56 | , libu = R10.Libu.Bu <| Just <| GoTo 10 57 | , theme = theme 58 | } 59 | , R10.Button.secondary [ width shrink ] 60 | { label = text "Jump To 0" 61 | , libu = R10.Libu.Bu <| Just <| GoTo 0 62 | , theme = theme 63 | } 64 | , R10.Button.secondary [ width shrink ] 65 | { label = text "Add 10" 66 | , libu = R10.Libu.Bu <| Just <| Add 10 67 | , theme = theme 68 | } 69 | , R10.Button.secondary [ width shrink ] 70 | { label = text "Add 94,851" 71 | , libu = R10.Libu.Bu <| Just <| Add 94857 72 | , theme = theme 73 | } 74 | , R10.Button.secondary [ width shrink ] 75 | { label = text "Subtract 10" 76 | , libu = R10.Libu.Bu <| Just <| Add -10 77 | , theme = theme 78 | } 79 | ] 80 | ] 81 | 82 | 83 | 84 | -- COUNTER 85 | 86 | 87 | subscriptions : Model -> Sub Msg 88 | subscriptions model = 89 | Sub.batch <| 90 | if R10.Counter.areMoving [ model.counter ] then 91 | [ Browser.Events.onAnimationFrame (\_ -> OnAnimationFrame) ] 92 | 93 | else 94 | [] 95 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/StateForValues.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.StateForValues exposing 2 | ( Entity(..) 3 | , EntityId 4 | , FieldId 5 | , Index 6 | , Value 7 | , encoderEntity 8 | , toString 9 | ) 10 | 11 | import Json.Encode as E 12 | 13 | 14 | type alias FieldId = 15 | String 16 | 17 | 18 | type alias EntityId = 19 | String 20 | 21 | 22 | type alias Value = 23 | String 24 | 25 | 26 | type alias Index = 27 | Int 28 | 29 | 30 | type Entity 31 | = EntityField FieldId Value 32 | | EntityMulti EntityId (List Entity) 33 | | EntityIndex Index (List Entity) 34 | 35 | 36 | encoderEntity : Entity -> E.Value 37 | encoderEntity entity = 38 | case entity of 39 | EntityField fieldId value -> 40 | -- E.string <| fieldId ++ ":: " ++ value 41 | E.object 42 | [ ( "id", E.string fieldId ) 43 | , ( "string", E.string value ) 44 | ] 45 | 46 | EntityMulti entityId listEntity -> 47 | E.object 48 | [ ( "id", E.string entityId ) 49 | , ( "list", E.list encoderEntity listEntity ) 50 | ] 51 | 52 | EntityIndex _ listEntity -> 53 | -- E.object 54 | -- [ ( "index", E.int index ) 55 | -- , ( "list", E.list encoderEntity listEntity ) 56 | -- ] 57 | E.list encoderEntity listEntity 58 | 59 | 60 | toString : List Entity -> String 61 | toString entities = 62 | E.encode 4 <| E.list encoderEntity entities 63 | 64 | 65 | 66 | -- 67 | -- Study on Firebase data structure 68 | -- 69 | -- https://firebase.google.com/docs/firestore/data-model#hierarchical-data 70 | -- 71 | -- type alias DocId = 72 | -- String 73 | -- 74 | -- 75 | -- type alias CollectionId = 76 | -- String 77 | -- 78 | -- 79 | -- type alias DocValue = 80 | -- String 81 | -- 82 | -- 83 | -- type Doc 84 | -- = Doc DocId DocValue (List Collection) 85 | -- 86 | -- 87 | -- type Collection 88 | -- = Collection CollectionId (List Doc) 89 | -- 90 | -- 91 | -- example : Collection 92 | -- example = 93 | -- Collection "rooms" 94 | -- [ Doc "roomA" 95 | -- "name: my chat room" 96 | -- [ Collection "messages" 97 | -- [ Doc "message1" "from: alex, msg: Hello world!" [] 98 | -- , Doc "message2" "..." [] 99 | -- ] 100 | -- ] 101 | -- , Doc "roomB" "..." [] 102 | -- ] 103 | -- 104 | -------------------------------------------------------------------------------- /examples/pwa/src/Pages/Form_Example_PhoneSelector.elm: -------------------------------------------------------------------------------- 1 | module Pages.Form_Example_PhoneSelector exposing 2 | ( Model 3 | , Msg 4 | , init 5 | , update 6 | , view 7 | ) 8 | 9 | import Element exposing (..) 10 | import R10.Form 11 | import R10.FormTypes 12 | import R10.Theme 13 | 14 | 15 | type alias Model = 16 | { phone1 : R10.Form.PhoneModel 17 | , phone2 : R10.Form.PhoneModel 18 | , disabled : Bool 19 | , messages : List String 20 | , valid : Maybe Bool 21 | } 22 | 23 | 24 | init : Model 25 | init = 26 | { phone1 = R10.Form.phoneInit 27 | , phone2 = R10.Form.phoneInit 28 | , disabled = False 29 | , messages = [] 30 | , valid = Nothing 31 | } 32 | 33 | 34 | type Msg 35 | = OnPhoneMsg1 R10.Form.PhoneMsg 36 | | OnPhoneMsg2 R10.Form.PhoneMsg 37 | 38 | 39 | update : Msg -> Model -> ( Model, Cmd Msg ) 40 | update msg model = 41 | case msg of 42 | OnPhoneMsg1 singleMsg -> 43 | let 44 | ( selectState, selectCmd ) = 45 | R10.Form.phoneUpdate singleMsg model.phone1 46 | in 47 | ( { model | phone1 = selectState }, Cmd.map OnPhoneMsg1 selectCmd ) 48 | 49 | OnPhoneMsg2 singleMsg -> 50 | let 51 | ( selectState, selectCmd ) = 52 | R10.Form.phoneUpdate singleMsg model.phone2 53 | in 54 | ( { model | phone2 = selectState }, Cmd.map OnPhoneMsg2 selectCmd ) 55 | 56 | 57 | view : Model -> R10.Theme.Theme -> List (Element Msg) 58 | view model theme = 59 | let 60 | palette : R10.FormTypes.Palette 61 | palette = 62 | R10.Form.themeToPalette theme 63 | in 64 | [ row 65 | [ spacing 40, width fill, padding 40 ] 66 | [ R10.Form.phoneView 67 | [] 68 | model.phone1 69 | { valid = model.valid 70 | , toMsg = OnPhoneMsg1 71 | , label = "Phone number" 72 | , helperText = Nothing 73 | , disabled = model.disabled 74 | , requiredLabel = Nothing 75 | , style = R10.Form.style.outlined 76 | , key = "field1" 77 | , palette = palette 78 | , countryOptions = Nothing 79 | } 80 | , R10.Form.phoneView 81 | [] 82 | model.phone2 83 | { valid = model.valid 84 | , toMsg = OnPhoneMsg2 85 | , label = "Phone number" 86 | , helperText = Nothing 87 | , disabled = model.disabled 88 | , requiredLabel = Nothing 89 | , style = R10.Form.style.filled 90 | , key = "field2" 91 | , palette = palette 92 | , countryOptions = Nothing 93 | } 94 | ] 95 | ] 96 | -------------------------------------------------------------------------------- /src/R10/Svg/Utils.elm: -------------------------------------------------------------------------------- 1 | module R10.Svg.Utils exposing (wrapperWithViewbox, wrapper32) 2 | 3 | {-| Utilities to render SVG elements. 4 | 5 | @docs wrapperWithViewbox, wrapper32 6 | 7 | -} 8 | 9 | import Element.WithContext exposing (..) 10 | import Html.Attributes 11 | import R10.Context exposing (..) 12 | import Svg 13 | import Svg.Attributes as SA 14 | 15 | 16 | 17 | -- WRAPPERS 18 | 19 | 20 | svgSize_ : String -> Int -> List (Svg.Attribute msg) 21 | svgSize_ viewbox ySize = 22 | -- 23 | -- IE11 is very picky about knowing all sizes of a SVG so 24 | -- here we calculate the width starting from the height 25 | -- using the viewbox. 26 | -- 27 | case String.split " " viewbox of 28 | [ x, y, dx, dy ] -> 29 | Maybe.withDefault [ SA.height <| String.fromInt ySize ] <| 30 | Maybe.map4 31 | (\_ _ dx_ dy_ -> 32 | let 33 | xSize : Float 34 | xSize = 35 | (dx_ * toFloat ySize) / dy_ 36 | in 37 | [ SA.height <| String.fromInt ySize 38 | , SA.width <| String.fromFloat xSize 39 | ] 40 | ) 41 | (String.toFloat x) 42 | (String.toFloat y) 43 | (String.toFloat dx) 44 | (String.toFloat dy) 45 | 46 | _ -> 47 | [ SA.height <| String.fromInt ySize ] 48 | 49 | 50 | {-| -} 51 | wrapperWithViewbox_ : String -> Int -> List (Svg.Svg msg) -> Svg.Svg msg 52 | wrapperWithViewbox_ viewbox ySize listSvg = 53 | Svg.svg 54 | ([ Html.Attributes.attribute "xmlns" "http://www.w3.org/2000/svg" 55 | , Html.Attributes.attribute "xmlns:xlink" "http://www.w3.org/1999/xlink" 56 | , SA.version "1.1" 57 | , SA.preserveAspectRatio "xMinYMin slice" 58 | , SA.viewBox viewbox 59 | 60 | -- This fix a bug in IE11: 61 | -- https://stackoverflow.com/questions/29393144/how-to-prevent-svg-elements-from-gaining-focus-with-tabs-in-ie11 62 | -- 63 | , Html.Attributes.attribute "focusable" "false" 64 | ] 65 | ++ svgSize_ viewbox ySize 66 | ) 67 | listSvg 68 | 69 | 70 | {-| -} 71 | wrapperWithViewbox : List (Attribute (R10.Context.ContextInternal z) msg) -> String -> Int -> List (Svg.Svg msg) -> Element (R10.Context.ContextInternal z) msg 72 | wrapperWithViewbox attrs viewbox size listSvg = 73 | el attrs <| html <| wrapperWithViewbox_ viewbox size listSvg 74 | 75 | 76 | {-| -} 77 | wrapper32 : List (Attribute (R10.Context.ContextInternal z) msg) -> Int -> List (Svg.Svg msg) -> Element (R10.Context.ContextInternal z) msg 78 | wrapper32 attrs size listSvg = 79 | wrapperWithViewbox attrs "0 0 32 32" size listSvg 80 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Application.elm: -------------------------------------------------------------------------------- 1 | module Application exposing (main) 2 | 3 | import Browser 4 | import Html 5 | import Html.Attributes 6 | import Json.Encode 7 | import Starter.Conf 8 | 9 | 10 | type Msg 11 | = OnUrlRequest 12 | | OnUrlChange 13 | 14 | 15 | type alias Flags = 16 | () 17 | 18 | 19 | type alias Model = 20 | () 21 | 22 | 23 | view : Model -> { body : List (Html.Html msg), title : String } 24 | view _ = 25 | { title = "title" 26 | , body = 27 | [ Html.div [ Html.Attributes.style "margin" "40px" ] 28 | [ Html.h1 [] [ Html.text "elm-starter configuration" ] 29 | , Html.pre 30 | [] 31 | [ Html.text <| 32 | Json.Encode.encode 4 <| 33 | Starter.Conf.conf 34 | -- From package.jspn 35 | { name = "NOT-AVAILABLE [name]" 36 | , nameLong = "NOT-AVAILABLE [nameLong]" 37 | , description = "NOT-AVAILABLE [description]" 38 | , author = "NOT-AVAILABLE [author]" 39 | , version = "NOT-AVAILABLE [version]" 40 | , homepage = "http://example.com/xxx/yyy" 41 | , license = "NOT-AVAILABLE [license]" 42 | , twitterSite = Just "NOT-AVAILABLE [twitterSite]" 43 | , twitterAuthor = Just "NOT-AVAILABLE [twitterAuthor]" 44 | , snapshotWidth = Just "NOT-AVAILABLE [snapshotWidth]" 45 | , snapshotHeight = Just "NOT-AVAILABLE [snapshotHeight]" 46 | , themeColor = Nothing 47 | 48 | -- From Git 49 | , commit = "NOT-AVAILABLE [commit]" 50 | , branch = "NOT-AVAILABLE [branch]" 51 | 52 | -- From starter.js 53 | , env = "NOT-AVAILABLE [env]" 54 | , dirPw = "NOT-AVAILABLE [dirPw]" 55 | , dirBin = "NOT-AVAILABLE [dirBin]" 56 | , dirIgnoredByGit = "NOT-AVAILABLE [dirIgnoredByGit]" 57 | , dirTemp = "NOT-AVAILABLE [dirTemp]" 58 | , fileElmWorker = "NOT-AVAILABLE [fileElmWorker]" 59 | } 60 | ] 61 | ] 62 | ] 63 | } 64 | 65 | 66 | main : Program Flags Model Msg 67 | main = 68 | Browser.application 69 | { init = \_ _ _ -> ( (), Cmd.none ) 70 | , view = view 71 | , update = \_ model -> ( model, Cmd.none ) 72 | , subscriptions = \_ -> Sub.none 73 | , onUrlRequest = \_ -> OnUrlRequest 74 | , onUrlChange = \_ -> OnUrlChange 75 | } 76 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Config.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Config exposing 2 | ( ColumnAttrs 3 | , ColumnConf 4 | , Config 5 | , FiltersConfig 6 | , HeaderInfo 7 | , PaginationConfig 8 | ) 9 | 10 | import Element.WithContext exposing (..) 11 | import R10.Context exposing (..) 12 | import R10.Palette 13 | import R10.Table.Internal.Msg 14 | import R10.Table.Internal.Types 15 | 16 | 17 | {-| There are quite a lot of ways to customize the `` tag. You can add 18 | a `` to group columns in weird ways. You can have a `` tag for 20 | summaries of various columns. And maybe you want to put attributes on `` 21 | or on particular rows in the body. All these customizations are available to you. 22 | -} 23 | 24 | 25 | 26 | --type alias Customizations data msg = 27 | -- { --columnHeaderView : List (HeaderData data msg) -> Element (R10.Context.ContextInternal z) msg 28 | -- --, 29 | -- bodyAttrs : List (Attribute (R10.Context.ContextInternal z) msg) 30 | -- , rowAttrsBuilder : Maybe data -> List (Attribute (R10.Context.ContextInternal z) msg) 31 | -- } 32 | 33 | 34 | type alias PaginationConfig = 35 | { lengthOptions : List Int } 36 | 37 | 38 | type alias FiltersConfig = 39 | { filterFields : List R10.Table.Internal.Types.Filter 40 | } 41 | 42 | 43 | 44 | -- CONFIG 45 | 46 | 47 | {-| Configuration for your table, describing your columns. 48 | 49 | **Note:** Your `Config` should _never_ be held in your model. 50 | It should only appear in `view` code. 51 | 52 | -- todo exposing `Config(..)` would be the same as using `type alias`? 53 | 54 | -} 55 | type alias Config data msg context = 56 | { toId : data -> String 57 | , toMsg : R10.Table.Internal.Msg.Msg -> msg 58 | , columns : List (ColumnConf data msg context) 59 | , bodyAttrs : List (Attribute (R10.Context.ContextInternal context) msg) 60 | , rowAttrsBuilder : Maybe data -> List (Attribute (R10.Context.ContextInternal context) msg) 61 | , maybePaginationConfig : Maybe PaginationConfig 62 | , maybeFiltersConfig : Maybe FiltersConfig 63 | } 64 | 65 | 66 | type alias ColumnConf data msg context = 67 | { name : String 68 | , viewCell : R10.Palette.Palette -> Maybe data -> Element (R10.Context.ContextInternal context) msg 69 | , viewHeader : R10.Palette.Palette -> HeaderInfo msg -> Element (R10.Context.ContextInternal context) msg 70 | , sorter : R10.Table.Internal.Types.Sorter data 71 | } 72 | 73 | 74 | type alias HeaderInfo msg = 75 | { name : String 76 | , sortStatus : R10.Table.Internal.Types.Status 77 | , onSortMsg : msg 78 | } 79 | 80 | 81 | type alias ColumnAttrs msg context = 82 | { header : List (Attribute (R10.Context.ContextInternal context) msg) 83 | , cell : List (Attribute (R10.Context.ContextInternal context) msg) 84 | } 85 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Sorter.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Sorter exposing 2 | ( increasingOrDecreasingBy 3 | , sort 4 | , unsortable 5 | ) 6 | 7 | import R10.Table.Internal.Config 8 | import R10.Table.Internal.State 9 | import R10.Table.Internal.Types 10 | 11 | 12 | 13 | -- SORTING 14 | 15 | 16 | sort : 17 | R10.Table.Internal.State.State 18 | -> List (R10.Table.Internal.Config.ColumnConf data msg context) 19 | -> List data 20 | -> List data 21 | sort state columnData data = 22 | case findSorter state.sort.name columnData of 23 | Nothing -> 24 | data 25 | 26 | Just sorter -> 27 | applySorter state.sort.isReversed sorter data 28 | 29 | 30 | applySorter : Bool -> R10.Table.Internal.Types.Sorter data -> List data -> List data 31 | applySorter isReversed sorter data = 32 | case sorter of 33 | R10.Table.Internal.Types.None -> 34 | data 35 | 36 | R10.Table.Internal.Types.Increasing sort_ -> 37 | sort_ data 38 | 39 | R10.Table.Internal.Types.Decreasing sort_ -> 40 | List.reverse (sort_ data) 41 | 42 | R10.Table.Internal.Types.IncOrDec sort_ -> 43 | if isReversed then 44 | List.reverse (sort_ data) 45 | 46 | else 47 | sort_ data 48 | 49 | R10.Table.Internal.Types.DecOrInc sort_ -> 50 | if isReversed then 51 | sort_ data 52 | 53 | else 54 | List.reverse (sort_ data) 55 | 56 | 57 | findSorter : 58 | String 59 | -> List (R10.Table.Internal.Config.ColumnConf data msg context) 60 | -> Maybe (R10.Table.Internal.Types.Sorter data) 61 | findSorter selectedColumn columnData = 62 | case columnData of 63 | [] -> 64 | Nothing 65 | 66 | { name, sorter } :: remainingColumnData -> 67 | if name == selectedColumn then 68 | Just sorter 69 | 70 | else 71 | findSorter selectedColumn remainingColumnData 72 | 73 | 74 | 75 | -- SORTERS 76 | 77 | 78 | {-| A sorter for columns that are unsortable. Maybe you have a column in your 79 | table for delete buttons that delete the row. It would not make any sense to 80 | sort based on that column. 81 | -} 82 | unsortable : R10.Table.Internal.Types.Sorter data 83 | unsortable = 84 | R10.Table.Internal.Types.None 85 | 86 | 87 | {-| Sometimes you want to be able to sort data in increasing _or_ decreasing 88 | order. Maybe you have race times for the 100 meter sprint. This function lets 89 | sort by best time by default, but also see the other order. 90 | 91 | sorter : R10.Table.Internal.State.Sorter { a | time : comparable } 92 | sorter = 93 | increasingOrDecreasingBy .time 94 | 95 | -} 96 | increasingOrDecreasingBy : (data -> comparable) -> R10.Table.Internal.Types.Sorter data 97 | increasingOrDecreasingBy toComparable = 98 | R10.Table.Internal.Types.IncOrDec (List.sortBy toComparable) 99 | -------------------------------------------------------------------------------- /src/R10/Paragraph.elm: -------------------------------------------------------------------------------- 1 | module R10.Paragraph exposing 2 | ( large, normal, small, xlarge, xxlarge, xsmall, xxsmall 3 | , normalMarkdown 4 | ) 5 | 6 | {-| Paraggraphs. 7 | 8 | @docs large, normal, small, xlarge, xxlarge, xsmall, xxsmall 9 | 10 | @docs normalMarkdown 11 | 12 | -} 13 | 14 | import Element.WithContext exposing (..) 15 | import R10.Context exposing (..) 16 | import R10.FontSize 17 | import R10.Link 18 | import R10.SimpleMarkdown 19 | 20 | 21 | spacingNormal : Int 22 | spacingNormal = 23 | 10 24 | 25 | 26 | {-| -} 27 | xxlarge : List (Attribute (R10.Context.ContextInternal z) msg) -> List (Element (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 28 | xxlarge attrs children = 29 | paragraph ([ R10.FontSize.xxlarge, spacing <| spacingNormal ] ++ attrs) children 30 | 31 | 32 | {-| -} 33 | xlarge : List (Attribute (R10.Context.ContextInternal z) msg) -> List (Element (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 34 | xlarge attrs children = 35 | paragraph ([ R10.FontSize.xlarge, spacing <| spacingNormal ] ++ attrs) children 36 | 37 | 38 | {-| -} 39 | large : List (Attribute (R10.Context.ContextInternal z) msg) -> List (Element (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 40 | large attrs children = 41 | paragraph ([ R10.FontSize.large, spacing <| spacingNormal ] ++ attrs) children 42 | 43 | 44 | {-| -} 45 | normalMarkdown : List (Attribute (R10.Context.ContextInternal z) msg) -> String -> Element (R10.Context.ContextInternal z) msg 46 | normalMarkdown attrs string = 47 | normal attrs 48 | (R10.SimpleMarkdown.elementMarkdownAdvanced 49 | { link = R10.Link.attrsUnderline } 50 | string 51 | ) 52 | 53 | 54 | {-| -} 55 | normal : List (Attribute (R10.Context.ContextInternal z) msg) -> List (Element (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 56 | normal attrs children = 57 | paragraph ([ R10.FontSize.normal, spacing <| spacingNormal ] ++ attrs) children 58 | 59 | 60 | {-| -} 61 | small : List (Attribute (R10.Context.ContextInternal z) msg) -> List (Element (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 62 | small attrs children = 63 | paragraph ([ R10.FontSize.small, spacing <| spacingNormal - 3 ] ++ attrs) children 64 | 65 | 66 | {-| -} 67 | xsmall : List (Attribute (R10.Context.ContextInternal z) msg) -> List (Element (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 68 | xsmall attrs children = 69 | paragraph ([ R10.FontSize.xsmall, spacing <| spacingNormal - 6 ] ++ attrs) children 70 | 71 | 72 | {-| -} 73 | xxsmall : List (Attribute (R10.Context.ContextInternal z) msg) -> List (Element (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 74 | xxsmall attrs children = 75 | paragraph ([ R10.FontSize.xsmall, spacing <| spacingNormal - 9 ] ++ attrs) children 76 | -------------------------------------------------------------------------------- /src/R10/ValidationDate.elm: -------------------------------------------------------------------------------- 1 | module R10.ValidationDate exposing 2 | ( Range 3 | , RangeResult(..) 4 | , dateToMillis 5 | , range 6 | ) 7 | 8 | import Iso8601 9 | import Parser 10 | import R10.Utils 11 | import Time 12 | 13 | 14 | prepareDate : String -> Result RangeResult String 15 | prepareDate value = 16 | -- Remove non-digits and extract year/month/day. 17 | -- Format the output as YYYY-MM-DD unless there 18 | -- errors. In which case it returns "" that will 19 | -- then fail later, during the conversion to date. 20 | let 21 | newValue : String 22 | newValue = 23 | value 24 | -- Remove all non-digits 25 | |> R10.Utils.userReplace "[^\\d]" (\_ -> "") 26 | -- Add an extra zero if needed, for example 27 | -- "2000/01/2" -> "2000/01/02" 28 | |> (\v -> 29 | if String.length v == 7 then 30 | String.left 6 v ++ "0" ++ String.right 1 v 31 | 32 | else 33 | v 34 | ) 35 | in 36 | if String.length newValue /= 8 then 37 | Err Need8Digits 38 | 39 | else 40 | let 41 | year : String 42 | year = 43 | String.slice 0 4 newValue 44 | 45 | month : String 46 | month = 47 | String.slice 4 6 newValue 48 | 49 | day : String 50 | day = 51 | String.slice 6 8 newValue 52 | in 53 | Ok <| year ++ "-" ++ month ++ "-" ++ day 54 | 55 | 56 | dateToMillis : String -> Result RangeResult Int 57 | dateToMillis date = 58 | case prepareDate date of 59 | Err rangeResult -> 60 | Err rangeResult 61 | 62 | Ok preparedDate -> 63 | case Iso8601.toTime preparedDate of 64 | Err iso8601Error -> 65 | Err <| ParsingError iso8601Error 66 | 67 | Ok posix -> 68 | Ok <| Time.posixToMillis posix 69 | 70 | 71 | type RangeResult 72 | = TooOld 73 | | TooNew 74 | | MinRangeNotValid 75 | | MaxRangeNotValid 76 | | InvertedMinMax 77 | | Need8Digits 78 | | ParsingError (List Parser.DeadEnd) 79 | 80 | 81 | type alias Range = 82 | { min : Int, max : Int } 83 | 84 | 85 | range : Range -> String -> Result RangeResult Int 86 | range { min, max } date = 87 | let 88 | maybePosixDate = 89 | dateToMillis date 90 | in 91 | case maybePosixDate of 92 | Err err -> 93 | Err err 94 | 95 | Ok posixDate -> 96 | if min > max then 97 | Err InvertedMinMax 98 | 99 | else if posixDate < min then 100 | Err TooOld 101 | 102 | else if posixDate > max then 103 | Err TooNew 104 | 105 | else 106 | Ok posixDate 107 | -------------------------------------------------------------------------------- /src/R10/Color/Utils.elm: -------------------------------------------------------------------------------- 1 | module R10.Color.Utils exposing (fromColorColor, toColorColor, fromHex, fromLightToDark, setAlpha, toCssRgba, fromHexToColorColor) 2 | 3 | {-| Utilities for colors. 4 | 5 | There are two competing color types in this library: 6 | 7 | - `Color.Color` (from `avh4/elm-color`) 8 | - `Color` (from `mdgriffith/elm-ui`) 9 | 10 | The default color type is `Color`. 11 | 12 | @docs fromColorColor, toColorColor, fromHex, fromLightToDark, setAlpha, toCssRgba, fromHexToColorColor 13 | 14 | -} 15 | 16 | import Color 17 | import Color.Convert 18 | import Color.Manipulate 19 | import Element.WithContext exposing (..) 20 | 21 | 22 | 23 | -- import R10.Context exposing (..) 24 | 25 | 26 | {-| Transform a `Color.Color` into an `Color`. 27 | -} 28 | fromColorColor : Color.Color -> Color 29 | fromColorColor color = 30 | let 31 | { red, green, blue, alpha } = 32 | Color.toRgba color 33 | in 34 | rgba red green blue alpha 35 | 36 | 37 | {-| Transform an `Color` into a `Color.Color`. 38 | -} 39 | toColorColor : Color -> Color.Color 40 | toColorColor elementColor = 41 | let 42 | { red, green, blue, alpha } = 43 | toRgb elementColor 44 | in 45 | Color.fromRgba { red = red, green = green, blue = blue, alpha = alpha } 46 | 47 | 48 | {-| Convert a string containing an hexadecimal number into a `Color.Color`. 49 | -} 50 | fromHexToColorColor : String -> Color.Color 51 | fromHexToColorColor hex = 52 | let 53 | resultColor : Result String Color.Color 54 | resultColor = 55 | Color.Convert.hexToColor hex 56 | 57 | color : Color.Color 58 | color = 59 | Result.withDefault (Color.fromRgba { red = 0, green = 0, blue = 0, alpha = 0 }) resultColor 60 | in 61 | color 62 | 63 | 64 | {-| Convert a string containing an hexadecimal number into an `Color`. 65 | -} 66 | fromHex : String -> Color 67 | fromHex hex = 68 | hex 69 | |> fromHexToColorColor 70 | |> fromColorColor 71 | 72 | 73 | {-| Convert an `Color` to a RGBA string, for example: rgba(100, 200, 0, 1) 74 | -} 75 | toCssRgba : Color -> String 76 | toCssRgba elementColor = 77 | elementColor 78 | |> toColorColor 79 | |> Color.Convert.colorToCssRgba 80 | 81 | 82 | {-| Convert a color from Light Mode to Dark Mode. This function works for `Color.Color` type. 83 | -} 84 | fromLightToDark : Color.Color -> Color.Color 85 | fromLightToDark color = 86 | color 87 | |> Color.Manipulate.scaleHsl 88 | { saturationScale = -0.17 89 | , lightnessScale = -0.04 90 | , alphaScale = 0 91 | } 92 | 93 | 94 | {-| Change the alpha channel in a color. This function works for `Color.Color` type. 95 | -} 96 | setAlpha : Float -> Color.Color -> Color.Color 97 | setAlpha newAlpha color = 98 | let 99 | c : { alpha : Float, blue : Float, green : Float, red : Float } 100 | c = 101 | Color.toRgba color 102 | in 103 | Color.fromRgba { red = c.red, green = c.green, blue = c.blue, alpha = newAlpha } 104 | -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/IconButton.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.IconButton exposing (view) 2 | 3 | import Element.WithContext exposing (..) 4 | import Element.WithContext.Background as Background 5 | import Element.WithContext.Border as Border 6 | import Html.Attributes 7 | import Html.Events 8 | import Json.Decode 9 | import R10.Context exposing (..) 10 | import R10.FormComponents.Internal.UI 11 | import R10.FormComponents.Internal.UI.Color 12 | import R10.Palette 13 | import R10.Transition 14 | 15 | 16 | view : 17 | List (Attribute (R10.Context.ContextInternal z) msg) 18 | -> 19 | { msgOnClick : Maybe msg 20 | , icon : Element (R10.Context.ContextInternal z) msg 21 | , palette : R10.Palette.Palette 22 | , size : Int 23 | } 24 | -> Element (R10.Context.ContextInternal z) msg 25 | view args { msgOnClick, icon, palette, size } = 26 | let 27 | padding_ : Int 28 | padding_ = 29 | 8 30 | 31 | containerSize : Int 32 | containerSize = 33 | 24 34 | 35 | iconHitboxSize : Int 36 | iconHitboxSize = 37 | size + (padding_ * 2) 38 | 39 | moveUp_ : Float 40 | moveUp_ = 41 | toFloat (iconHitboxSize - containerSize) / 2 42 | 43 | attrsCommon : List (Attr (R10.Context.ContextInternal z) () msg) 44 | attrsCommon = 45 | [ Background.color <| R10.FormComponents.Internal.UI.Color.onSurfaceA 0 palette 46 | , padding padding_ 47 | , centerX 48 | 49 | -- achieving `centerY` here. Note: we cannot use `moveUp` because `Html.Attributes.class <| "ripple"` brakes it 50 | , htmlAttribute <| Html.Attributes.style "margin-top" ("-" ++ String.fromFloat moveUp_ ++ "px") 51 | ] 52 | 53 | attrsClickable : List (Attr (R10.Context.ContextInternal z) () msg) 54 | attrsClickable = 55 | case msgOnClick of 56 | Just msgOnClick_ -> 57 | [ htmlAttribute <| Html.Attributes.tabindex 0 58 | , htmlAttribute <| R10.FormComponents.Internal.UI.onSelectKey msgOnClick_ 59 | , htmlAttribute <| Html.Events.stopPropagationOn "mouseup" (Json.Decode.succeed ( msgOnClick_, False )) 60 | , htmlAttribute <| Html.Attributes.class <| "ripple" 61 | , R10.Transition.transition "all 0.13s; margin-top 0s " 62 | , pointer 63 | , Border.rounded 40 64 | , mouseOver [ Border.innerShadow { offset = ( 0, 0 ), size = 40, blur = 0, color = R10.FormComponents.Internal.UI.Color.onSurfaceA 0.07 palette } ] 65 | , focused [ Background.color <| R10.FormComponents.Internal.UI.Color.onSurfaceA 0.14 palette ] 66 | ] 67 | 68 | Nothing -> 69 | [ alpha 0.5 ] 70 | in 71 | el 72 | ([ width <| px containerSize 73 | , height <| px containerSize 74 | ] 75 | ++ args 76 | ) 77 | <| 78 | el 79 | (attrsCommon ++ attrsClickable) 80 | <| 81 | el [ centerX, centerY, width <| px size, height <| px size ] <| 82 | icon 83 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/ServiceWorker.elm: -------------------------------------------------------------------------------- 1 | module Starter.ServiceWorker exposing 2 | ( encoderCacheableUrls 3 | , precacheFiles 4 | , serviceWorker 5 | ) 6 | 7 | import Json.Encode 8 | import Starter.Cache 9 | import Starter.ConfMeta 10 | 11 | 12 | encoderCacheableUrls : { a | revision : String, url : String } -> Json.Encode.Value 13 | encoderCacheableUrls obj = 14 | Json.Encode.object 15 | [ ( "url", Json.Encode.string obj.url ) 16 | , ( "revision", Json.Encode.string obj.revision ) 17 | ] 18 | 19 | 20 | precacheFiles : 21 | { assets : List ( String, String ) 22 | , commit : String 23 | , relative : String 24 | , version : String 25 | } 26 | -> String 27 | precacheFiles { relative, version, commit, assets } = 28 | Starter.Cache.stuffToCache relative version commit assets 29 | |> List.map (\( url, revision ) -> { url = url, revision = revision ++ "." ++ commit }) 30 | |> Json.Encode.list encoderCacheableUrls 31 | |> Json.Encode.encode 4 32 | 33 | 34 | serviceWorker : 35 | { assets : List ( String, String ) 36 | , commit : String 37 | , relative : String 38 | , version : String 39 | } 40 | -> String 41 | serviceWorker { relative, version, commit, assets } = 42 | "// " 43 | ++ Starter.ConfMeta.confMeta.messageDoNotEditDisclaimer 44 | ++ """ 45 | // 46 | // This is implemented using Workbox 47 | // https://developers.google.com/web/tools/workbox 48 | // 49 | 50 | importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js'); 51 | 52 | const registerRoute = workbox.routing.registerRoute; 53 | const NetworkFirst = workbox.strategies.NetworkFirst; 54 | const CacheFirst = workbox.strategies.CacheFirst; 55 | const StaleWhileRevalidate = workbox.strategies.StaleWhileRevalidate; 56 | const ExpirationPlugin = workbox.expiration.ExpirationPlugin; 57 | const precacheAndRoute = workbox.precaching.precacheAndRoute; 58 | 59 | // https://developers.google.com/web/tools/workbox/guides/precache-files 60 | precacheAndRoute( 61 | """ 62 | ++ precacheFiles 63 | { assets = assets 64 | , commit = commit 65 | , relative = relative 66 | , version = version 67 | } 68 | ++ """ 69 | ); 70 | 71 | 72 | registerRoute( 73 | ({url}) => url.pathname === ('/'), 74 | new NetworkFirst() 75 | ); 76 | 77 | registerRoute( 78 | ({request}) => request.destination === 'script', 79 | new NetworkFirst() 80 | ); 81 | 82 | registerRoute( 83 | // Cache style assets, i.e. CSS files. 84 | ({request}) => request.destination === 'style', 85 | // Use cache but update in the background. 86 | new StaleWhileRevalidate({ 87 | // Use a custom cache name. 88 | cacheName: 'css-cache', 89 | }) 90 | ); 91 | 92 | // From https://developers.google.com/web/tools/workbox/guides/common-recipes 93 | registerRoute( 94 | ({request}) => request.destination === 'image', 95 | new CacheFirst({ 96 | cacheName: 'images', 97 | plugins: [ 98 | new ExpirationPlugin({ 99 | maxEntries: 60, 100 | maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days 101 | }), 102 | ], 103 | }) 104 | ); 105 | """ 106 | -------------------------------------------------------------------------------- /src/R10/LanguageSelector.elm: -------------------------------------------------------------------------------- 1 | module R10.LanguageSelector exposing (view) 2 | 3 | {-| Dropdown for the language selector 4 | 5 | @docs view 6 | 7 | -} 8 | 9 | import Element.WithContext exposing (..) 10 | import Element.WithContext.Font as Font 11 | import Html.Attributes 12 | import R10.Color.Utils 13 | import R10.Context exposing (..) 14 | import R10.DropDown 15 | import R10.Language 16 | import R10.Svg.Icons 17 | 18 | 19 | {-| -} 20 | view : 21 | List (Attribute (R10.Context.ContextInternal z) msg) 22 | -> 23 | { changeMsg : Result String R10.Language.Language -> msg 24 | , colorBackground : Color 25 | , colorFont : Color 26 | , currentLanguage : R10.Language.Language 27 | , supportedLanguages : List R10.Language.Language 28 | , fontSize : Int 29 | } 30 | -> Element (R10.Context.ContextInternal z) msg 31 | view attrs args = 32 | let 33 | handleInputChange : String -> msg 34 | handleInputChange string = 35 | string 36 | |> R10.Language.decoder args.supportedLanguages 37 | |> args.changeMsg 38 | 39 | optionList : List R10.DropDown.Option 40 | optionList = 41 | args.supportedLanguages 42 | |> List.map 43 | (\locale -> 44 | R10.DropDown.Option 45 | (R10.Language.toString locale) 46 | (R10.Language.toStringLong R10.Language.Localization locale) 47 | ) 48 | in 49 | el 50 | (attrs 51 | ++ [ inFront <| 52 | row 53 | [ spacing 7 54 | , htmlAttribute <| Html.Attributes.style "pointer-events" "none" 55 | , htmlAttribute <| Html.Attributes.style "letter-spacing" "0px" 56 | , centerY 57 | , moveRight 2 58 | , Font.size args.fontSize 59 | , Font.color args.colorFont 60 | ] 61 | [ R10.Svg.Icons.world_l [] args.colorFont args.fontSize 62 | , el [ Font.color <| rgba 0 0 0 0 ] <| text <| R10.Language.toStringLong R10.Language.Localization args.currentLanguage 63 | , triangle args.colorFont 64 | ] 65 | ] 66 | ) 67 | (R10.DropDown.viewBorderLess [] 68 | { colorBackground = args.colorBackground 69 | , colorFont = args.colorFont 70 | , currentValue = R10.Language.toString args.currentLanguage 71 | , inputHandler = handleInputChange 72 | , optionList = optionList 73 | , fontSize = args.fontSize 74 | } 75 | ) 76 | 77 | 78 | getBorderAttr : Int -> String -> String 79 | getBorderAttr width colorHex = 80 | String.join " " [ String.fromInt width ++ "px", "solid", colorHex ] 81 | 82 | 83 | triangle : Color -> Element (R10.Context.ContextInternal z) msg 84 | triangle colorFont = 85 | el 86 | (List.map htmlAttribute <| 87 | [ Html.Attributes.style "width" "0" 88 | , Html.Attributes.style "height" "0" 89 | , Html.Attributes.style "border-left" <| getBorderAttr 4 "transparent" 90 | , Html.Attributes.style "border-right" <| getBorderAttr 4 "transparent" 91 | , Html.Attributes.style "border-top" <| getBorderAttr 6 (R10.Color.Utils.toCssRgba colorFont) 92 | 93 | -- , Html.Attributes.style "position" "absolute" 94 | -- , Html.Attributes.style "right" "12px" 95 | , Html.Attributes.style "pointer-events" "none" 96 | ] 97 | ) 98 | none 99 | -------------------------------------------------------------------------------- /PUBLISHING.md: -------------------------------------------------------------------------------- 1 | # PUBLISHING 2 | 3 | ## Documentation 4 | 5 | You can verify the documentation of the project running 6 | 7 | ``` 8 | elm-doc-preview 9 | ``` 10 | 11 | installable with 12 | ``` 13 | npm install -g elm-doc-preview 14 | ``` 15 | 16 | These are good guidelines to follow while coding and writing documentation: 17 | 18 | * https://package.elm-lang.org/help/design-guidelines 19 | * https://elm-lang.org/docs/style-guide 20 | 21 | ## Dependencies 22 | 23 | You can add or remove dependencies with `elm-json` 24 | 25 | ## Publishing 26 | 27 | **Notes about elm-bump: Read below about the error "I got the data back, but it was not what I was expecting"** 28 | 29 | Run the following to bump && publish the version in elm.json: 30 | 31 | ``` 32 | npx -p elm@0.19.0-no-deps elm bump 33 | ``` 34 | 35 | or 36 | 37 | ``` 38 | elm bump 39 | ``` 40 | 41 | --- 42 | 43 | If you get something like this: 44 | 45 | ``` 46 | -- PROBLEM LOADING DOCS -------------------------------------------------------- 47 | 48 | I need the docs for 12.17.0 to compute the next version number, so I fetched: 49 | 50 | https://package.elm-lang.org/packages/NoRedInk/noredink-ui/12.17.0/docs.json 51 | 52 | I got the data back, but it was not what I was expecting. The response body 53 | contains 195076 bytes. Here is the beginning: 54 | 55 | [{"name":"Nri.Ui","comment":" A collection of helpers for working with No... 56 | 57 | Does this error keep showing up? Maybe there is something weird with your 58 | internet connection. We have gotten reports that schools, businesses, airports, 59 | etc. sometimes intercept requests and add things to the body or change its 60 | contents entirely. Could that be the problem? 61 | ``` 62 | 63 | Then run it with 0.19.0 explicitly (0.19.1 has some problems with big docs): 64 | 65 | ``` 66 | npx -p elm@0.19.0-no-deps elm bump 67 | ``` 68 | 69 | --- 70 | 71 | Commit and push your changes in a PR. Once it's approved and merged, then: 72 | 73 | ``` 74 | git tag -a 7.0.0 -m "release version 7.0.0" 75 | git push origin 7.0.0 76 | npx -p elm@0.19.0-no-deps elm publish 77 | ``` 78 | 79 | You can also add a tag in https://github.com/rakutentech/r10/releases/new if you want to add more detail. 80 | 81 | Once you've published, you should see the latest version at https://package.elm-lang.org/packages/rakutentech/r10/ or https://elm.dmy.fr/packages/rakutentech/r10/. 82 | 83 | ## Best practices 84 | 85 | Imports should be plain, without aliases and without exposing particular functions. This make the code more clear as it immediately understanding if a resource is defined in the present module or somewhere else. It also makes code block more portable. 86 | 87 | These are the exceptions to this rule: 88 | 89 | import Element exposing (..) 90 | import Element.Background as Background 91 | import Element.Border as Border 92 | import Element.Events as Events 93 | import Element.Font as Font 94 | import Element.Input as Input 95 | import Element.Keyed as Keyed 96 | import Json.Decode as D 97 | import Json.Encode as E 98 | import Svg.Attributes as SA 99 | import Html exposing (..) 100 | import Html.Attributes exposing (..) 101 | 102 | `Html` and `Html.Attributes` can be esposed only if `Element` is not used, otherwise there are conflicts. 103 | 104 | ---- 105 | 106 | Modules that are not exposed should have `Internal` somewhere in their path. 107 | 108 | ## How to develop 109 | 110 | For testing or modification to R10 on a local environment, clone R10 (https://github.com/rakutentech/r10) in a sibling folder of projects that use R10, then, in the elm.json file of such project: 111 | 112 | * Remove the dependency from r10 (or run `elm-json uninstall rakutenteck/r10`) 113 | * Add in the list of `"source-directories"` folders: `"../r10/src"` -------------------------------------------------------------------------------- /src/R10/Alert.elm: -------------------------------------------------------------------------------- 1 | module R10.Alert exposing (Alert(..), view) 2 | 3 | import Element.WithContext exposing (..) 4 | import Element.WithContext.Border as Border 5 | import R10.Color.AttrsBackground 6 | import R10.Color.AttrsFont 7 | import R10.Color.Svg 8 | import R10.Context 9 | import R10.FontSize 10 | import R10.Svg.Icons 11 | 12 | 13 | type Alert 14 | = Danger 15 | | Info 16 | | Warning 17 | | Success 18 | 19 | 20 | view : 21 | Alert 22 | -> List (Attribute (R10.Context.ContextInternal z) msg) 23 | -> List (Element (R10.Context.ContextInternal z) msg) 24 | -> Element (R10.Context.ContextInternal z) msg 25 | view alert attrs listOfElement = 26 | if List.isEmpty listOfElement then 27 | text "" 28 | 29 | else 30 | withContext <| 31 | \c -> 32 | let 33 | ( colorFont, colorBackground, colorIcon ) = 34 | case alert of 35 | Danger -> 36 | ( R10.Color.AttrsFont.fontAlertDanger 37 | , R10.Color.AttrsBackground.backgroundAlertDanger 38 | , R10.Color.Svg.fontAlertDanger c.contextR10.theme 39 | ) 40 | 41 | Info -> 42 | ( R10.Color.AttrsFont.fontAlertInfo 43 | , R10.Color.AttrsBackground.backgroundAlertInfo 44 | , R10.Color.Svg.fontAlertInfo c.contextR10.theme 45 | ) 46 | 47 | Warning -> 48 | ( R10.Color.AttrsFont.fontAlertWarning 49 | , R10.Color.AttrsBackground.backgroundAlertWarning 50 | , R10.Color.Svg.fontAlertWarning c.contextR10.theme 51 | ) 52 | 53 | Success -> 54 | ( R10.Color.AttrsFont.fontAlertSuccess 55 | , R10.Color.AttrsBackground.backgroundAlertSuccess 56 | , R10.Color.Svg.fontAlertSuccess c.contextR10.theme 57 | ) 58 | in 59 | column [ spacing 20, width fill ] 60 | (List.map 61 | (\section -> 62 | row 63 | [ colorFont 64 | , colorBackground 65 | , R10.FontSize.small 66 | , Border.rounded 5 67 | , padding 16 68 | , spacing 16 69 | , width fill 70 | ] 71 | [ (case alert of 72 | -- https://dev-cdn.rex.contents.rakuten.co.jp/rex-form/v1.7.0/components/alert.html 73 | Danger -> 74 | R10.Svg.Icons.sign_warning_l 75 | 76 | Info -> 77 | R10.Svg.Icons.sign_info_l 78 | 79 | Warning -> 80 | R10.Svg.Icons.sign_info_l 81 | 82 | Success -> 83 | R10.Svg.Icons.check 84 | ) 85 | [ alignTop ] 86 | colorIcon 87 | 16 88 | , paragraph attrs [ section ] 89 | ] 90 | ) 91 | listOfElement 92 | ) 93 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/ElmGo.elm: -------------------------------------------------------------------------------- 1 | module Starter.ElmGo exposing 2 | ( Compilation(..) 3 | , ElmLiveArgs 4 | , HotReload(..) 5 | , Pushstate(..) 6 | , Reload(..) 7 | , Ssl(..) 8 | , Verbose(..) 9 | , elmGo 10 | , encoder 11 | ) 12 | 13 | import Json.Encode 14 | 15 | 16 | type alias ElmLiveArgs = 17 | { dir : String 18 | , outputCompiledJs : String 19 | , indexHtml : String 20 | , port_ : Int 21 | , compilation : Compilation 22 | , verbose : Verbose 23 | , pushstate : Pushstate 24 | , reload : Reload 25 | , hotReload : HotReload 26 | , ssl : Ssl 27 | , elmFileToCompile : String 28 | , dirBin : String 29 | , relative : String 30 | , certificatesFolder : String 31 | } 32 | 33 | 34 | type Compilation 35 | = Optimize 36 | | Normal 37 | | Debug 38 | 39 | 40 | type Reload 41 | = ReloadYes 42 | | ReloadNo 43 | 44 | 45 | type HotReload 46 | = HotReloadYes 47 | | HotReloadNo 48 | 49 | 50 | type Verbose 51 | = VerboseYes 52 | | VerboseNo 53 | 54 | 55 | type Pushstate 56 | = PushstateYes 57 | | PushstateNo 58 | 59 | 60 | type Ssl 61 | = SslYes 62 | | SslNo 63 | 64 | 65 | elmGo : ElmLiveArgs -> { command : String, parameters : List String } 66 | elmGo args = 67 | -- { command = args.dirBin ++ "/elm-go" 68 | { command = "node" 69 | , parameters = 70 | [ "node_modules/.bin/elm-go" 71 | 72 | -- [ "elm-go/bin/elm-go.js" 73 | , args.elmFileToCompile 74 | , "--path-to-elm=" ++ args.dirBin ++ "/elm" 75 | , "--dir=" ++ args.dir 76 | , "--start-page=" ++ args.indexHtml 77 | , "--port=" ++ String.fromInt args.port_ 78 | ] 79 | ++ (case args.ssl of 80 | SslYes -> 81 | [ "--ssl" 82 | 83 | -- , "--ssl-cert=" ++ args.certificatesFolder ++ "/localhost.crt" 84 | -- , "--ssl-key=" ++ args.certificatesFolder ++ "/localhost.key" 85 | ] 86 | 87 | SslNo -> 88 | [] 89 | ) 90 | ++ (case args.pushstate of 91 | PushstateYes -> 92 | [ "--pushstate" ] 93 | 94 | PushstateNo -> 95 | [] 96 | ) 97 | ++ (case args.verbose of 98 | VerboseYes -> 99 | [ "--verbose" ] 100 | 101 | VerboseNo -> 102 | [] 103 | ) 104 | ++ (case args.hotReload of 105 | HotReloadYes -> 106 | [ "--hot" ] 107 | 108 | HotReloadNo -> 109 | [] 110 | ) 111 | ++ (case args.reload of 112 | ReloadYes -> 113 | [] 114 | 115 | ReloadNo -> 116 | [ "--no-reload" ] 117 | ) 118 | ++ [ "--" 119 | , "--output=" ++ args.outputCompiledJs 120 | ] 121 | ++ (case args.compilation of 122 | Optimize -> 123 | [ "--optimize" ] 124 | 125 | Normal -> 126 | [] 127 | 128 | Debug -> 129 | [ "--debug" ] 130 | ) 131 | } 132 | 133 | 134 | encoder : { command : String, parameters : List String } -> Json.Encode.Value 135 | encoder args = 136 | Json.Encode.object 137 | [ ( "command", Json.Encode.string args.command ) 138 | , ( "parameters", Json.Encode.list Json.Encode.string args.parameters ) 139 | ] 140 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 3.0.0 (2021-02-19) 2 | 3 | ## Release Highlights 4 | 5 | * Updates color system, making Element.Color the default color. 6 | * Add single radio row. 7 | 8 | ## Differences 9 | 10 | This is a MAJOR change. 11 | 12 | ---- R10.Color.Svg - MAJOR ---- 13 | 14 | Added: 15 | fontHighEmphasis : R10.Theme.Theme -> Element.Color 16 | fontMediumEmphasis : R10.Theme.Theme -> Element.Color 17 | 18 | Removed: 19 | fontNormal : Theme -> Color 20 | logoHex : Theme -> Color 21 | mediumEmphasis : Theme -> Color 22 | 23 | 24 | ---- R10.Color.Utils - MAJOR ---- 25 | 26 | Added: 27 | fromColorColor : Color.Color -> Element.Color 28 | fromHexToColorColor : String.String -> Color.Color 29 | toColorColor : Element.Color -> Color.Color 30 | toCssRgba : Element.Color -> String.String 31 | 32 | Removed: 33 | colorToElementColor : Color -> Color 34 | elementColorToColor : Color -> Color 35 | toHex : Color -> String 36 | 37 | 38 | ---- R10.Form - MINOR ---- 39 | 40 | Added: 41 | type alias TextArgs msg = R10.FormComponents.Internal.Text.Args msg 42 | textView : 43 | List.List (Element.Attribute msg) 44 | -> List.List (Element.Attribute msg) 45 | -> R10.FormComponents.Internal.Text.Args msg 46 | -> Element.Element msg 47 | 48 | 49 | ---- R10.FormTypes - MAJOR ---- 50 | 51 | Changed: 52 | - type TypeSingle = SingleRadio | SingleCombobox | SingleSelect 53 | + type TypeSingle 54 | = SingleRadio 55 | | SingleRadioRow 56 | | SingleCombobox 57 | | SingleSelect 58 | 59 | - inputField : 60 | { binaryCheckbox : FieldType 61 | , binarySwitch : FieldType 62 | , multiCombobox : List FieldOption -> FieldType 63 | , singleCombobox : List FieldOption -> FieldType 64 | , singleSelect : List FieldOption -> FieldType 65 | , singleRadio : List FieldOption -> FieldType 66 | , textEmail : FieldType 67 | , textMultiline : FieldType 68 | , textPasswordCurrent : FieldType 69 | , textPasswordNew : FieldType 70 | , textPlain : FieldType 71 | , textUsername : FieldType 72 | , textWithPattern : String -> FieldType 73 | } 74 | + inputField : 75 | { binaryCheckbox : R10.FormTypes.FieldType 76 | , binarySwitch : R10.FormTypes.FieldType 77 | , multiCombobox : 78 | List.List R10.FormTypes.FieldOption -> R10.FormTypes.FieldType 79 | , singleCombobox : 80 | List.List R10.FormTypes.FieldOption -> R10.FormTypes.FieldType 81 | , singleSelect : 82 | List.List R10.FormTypes.FieldOption -> R10.FormTypes.FieldType 83 | , singleRadio : 84 | List.List R10.FormTypes.FieldOption -> R10.FormTypes.FieldType 85 | , singleRadioRow : 86 | List.List R10.FormTypes.FieldOption -> R10.FormTypes.FieldType 87 | , textEmail : R10.FormTypes.FieldType 88 | , textMultiline : R10.FormTypes.FieldType 89 | , textPasswordCurrent : R10.FormTypes.FieldType 90 | , textPasswordNew : R10.FormTypes.FieldType 91 | , textPlain : R10.FormTypes.FieldType 92 | , textUsername : R10.FormTypes.FieldType 93 | , textWithPattern : String.String -> R10.FormTypes.FieldType 94 | } 95 | 96 | 97 | 98 | ---- R10.Svg.IconsExtra - MINOR ---- 99 | 100 | Added: 101 | copy : 102 | List.List (Element.Attribute msg) 103 | -> Element.Color 104 | -> Basics.Int 105 | -> Element.Element msg -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/Phone/Common.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.Phone.Common exposing 2 | ( Args 3 | , FieldOption 4 | , Model 5 | , Msg(..) 6 | , dropdownContainerId 7 | , dropdownContentId 8 | , dropdownSearchBoxId 9 | , filterBySearch 10 | , init 11 | , inputPhoneElementId 12 | , normalizeString 13 | ) 14 | 15 | import Element.WithContext exposing (..) 16 | import R10.Context exposing (..) 17 | import R10.Country 18 | import R10.FormComponents.Internal.Single.Common 19 | import R10.FormComponents.Internal.Style 20 | import R10.Palette 21 | 22 | 23 | 24 | -- types 25 | 26 | 27 | type Msg 28 | = NoOp 29 | | OnFocus String 30 | | OnLoseFocus String 31 | | OnScroll Float 32 | | OnEsc String Bool 33 | -- 34 | | OnInputClick { key : String, selectedY : Float } 35 | | OnOptionSelect String R10.Country.Country 36 | | OnSearch { key : String, selectOptionHeight : Int, maxDisplayCount : Int, filteredFieldOption : List R10.Country.Country } String 37 | | OnArrowUp { key : String, selectOptionHeight : Int, maxDisplayCount : Int, filteredFieldOption : List R10.Country.Country } 38 | | OnArrowDown { key : String, selectOptionHeight : Int, maxDisplayCount : Int, filteredFieldOption : List R10.Country.Country } 39 | -- 40 | | OnValueChange String { selectOptionHeight : Int, maxDisplayCount : Int, filteredFieldOption : List R10.Country.Country } String 41 | | OnSimpleValueChange Bool String 42 | 43 | 44 | init : Model 45 | init = 46 | R10.FormComponents.Internal.Single.Common.init 47 | 48 | 49 | type alias Model = 50 | R10.FormComponents.Internal.Single.Common.Model 51 | 52 | 53 | type alias FieldOption = 54 | R10.FormComponents.Internal.Single.Common.FieldOption 55 | 56 | 57 | type alias Args z msg = 58 | -- Stuff that change 59 | { maybeValid : Maybe Bool 60 | 61 | ---- Messages 62 | , toMsg : Msg -> msg 63 | 64 | -- Stuff that doesn't change 65 | , label : String 66 | , helperText : Maybe String 67 | , disabled : Bool 68 | , requiredLabel : Maybe String 69 | , style : R10.FormComponents.Internal.Style.Style 70 | , key : String 71 | , palette : R10.Palette.Palette 72 | , disabledCountryChange : Bool 73 | 74 | -- Specific 75 | , countryOptions : List R10.Country.Country 76 | , toOptionEl : R10.Country.Country -> Element (R10.Context.ContextInternal z) msg 77 | , selectOptionHeight : Int 78 | , maxDisplayCount : Int 79 | , leadingIcon : List (Element (R10.Context.ContextInternal z) msg) 80 | , trailingIcon : List (Element (R10.Context.ContextInternal z) msg) 81 | } 82 | 83 | 84 | normalizeString : String -> String 85 | normalizeString = 86 | -- use for filtering, search <-> label comparison 87 | String.toLower >> String.trim 88 | 89 | 90 | searchFn : String -> R10.Country.Country -> Bool 91 | searchFn search country = 92 | String.contains 93 | (search |> normalizeString) 94 | (country |> R10.Country.toCountryNameWithAlias |> normalizeString) 95 | 96 | 97 | filterBySearch : String -> List R10.Country.Country -> List R10.Country.Country 98 | filterBySearch search fieldOptions = 99 | if String.isEmpty search then 100 | fieldOptions 101 | 102 | else 103 | fieldOptions 104 | |> List.filter (searchFn search) 105 | 106 | 107 | dropdownContainerId : String -> String 108 | dropdownContainerId key = 109 | "dropdown-container-" ++ key 110 | 111 | 112 | dropdownContentId : String -> String 113 | dropdownContentId key = 114 | "dropdown-content-" ++ key 115 | 116 | 117 | dropdownSearchBoxId : String -> String 118 | dropdownSearchBoxId key = 119 | "dropdown-search-" ++ key 120 | 121 | 122 | inputPhoneElementId : String -> String 123 | inputPhoneElementId key = 124 | "input-phone-" ++ key 125 | -------------------------------------------------------------------------------- /src/R10/Color.elm: -------------------------------------------------------------------------------- 1 | module R10.Color exposing 2 | ( Base 3 | , Primary, primary, primaryDefault, primaryToString, primaryDecoder, primaryDecoderExploration 4 | , Derived 5 | , listPrimary, listBase, listDerived 6 | , maximumContrast 7 | ) 8 | 9 | {-| These lists are just to be used to create documentations, not to actually use colors in the layout. 10 | 11 | 12 | # Base 13 | 14 | Base colors are the only color, together with one primary color, that are used to derive all other colors for the interface. 15 | 16 | They are different depending on the mode: 17 | 18 | ![Colors](https://r10.netlify.app/images/base_500.png) 19 | 20 | @docs Base 21 | 22 | 23 | # Primary 24 | 25 | These represent the brands color of Rakuten. 26 | 27 | Rakuten Brand guideline: 28 | 29 | ![Colors](https://r10.netlify.app/images/colors-overview400.png) 30 | 31 | @docs Primary, primary, primaryDefault, primaryToString, primaryDecoder, primaryDecoderExploration 32 | 33 | 34 | # Derived 35 | 36 | @docs Derived 37 | 38 | 39 | # Lists 40 | 41 | These lists should only be used to generate documentation. 42 | 43 | @docs listPrimary, listBase, listDerived 44 | 45 | 46 | # Utils 47 | 48 | @docs maximumContrast 49 | 50 | -} 51 | 52 | import Color 53 | import Json.Decode 54 | import Json.Decode.Exploration 55 | import R10.Color.Internal.Base 56 | import R10.Color.Internal.Derived 57 | import R10.Color.Internal.Primary 58 | import R10.Theme 59 | 60 | 61 | 62 | -- EXPOSING PRIMARY COLOR STUFF 63 | 64 | 65 | {-| -} 66 | type alias Primary = 67 | R10.Color.Internal.Primary.Color 68 | 69 | 70 | {-| -} 71 | primary : 72 | { crimsonRed : Primary 73 | , red : Primary 74 | , orange : Primary 75 | , yellow : Primary 76 | , green : Primary 77 | , lightBlue : Primary 78 | , blue : Primary 79 | , blueSky : Primary 80 | , purple : Primary 81 | , pink : Primary 82 | } 83 | primary = 84 | { yellow = R10.Color.Internal.Primary.Yellow 85 | , purple = R10.Color.Internal.Primary.Purple 86 | , pink = R10.Color.Internal.Primary.Pink 87 | , orange = R10.Color.Internal.Primary.Orange 88 | , lightBlue = R10.Color.Internal.Primary.LightBlue 89 | , green = R10.Color.Internal.Primary.Green 90 | , crimsonRed = R10.Color.Internal.Primary.CrimsonRed 91 | , red = R10.Color.Internal.Primary.CrimsonRed 92 | , blue = R10.Color.Internal.Primary.Blue 93 | , blueSky = R10.Color.Internal.Primary.BlueSky 94 | } 95 | 96 | 97 | {-| -} 98 | primaryDecoder : Json.Decode.Decoder Primary 99 | primaryDecoder = 100 | R10.Color.Internal.Primary.decoder 101 | 102 | 103 | {-| -} 104 | primaryDecoderExploration : Json.Decode.Exploration.Decoder Primary 105 | primaryDecoderExploration = 106 | R10.Color.Internal.Primary.decoderExploration 107 | 108 | 109 | {-| -} 110 | primaryToString : Primary -> String 111 | primaryToString = 112 | R10.Color.Internal.Primary.toString 113 | 114 | 115 | {-| -} 116 | primaryDefault : Primary 117 | primaryDefault = 118 | R10.Color.Internal.Primary.default 119 | 120 | 121 | 122 | -- EXPOSING BASE COLOR STUFF 123 | 124 | 125 | {-| -} 126 | type alias Base = 127 | R10.Color.Internal.Base.Color 128 | 129 | 130 | 131 | -- EXPOSING DERIVED COLOR STUFF 132 | 133 | 134 | {-| -} 135 | type alias Derived = 136 | R10.Color.Internal.Derived.Color 137 | 138 | 139 | 140 | -- LISTS 141 | 142 | 143 | {-| -} 144 | listPrimary : R10.Theme.Theme -> List { color : Color.Color, name : String, description : String, type_ : Primary } 145 | listPrimary = 146 | R10.Color.Internal.Primary.list 147 | 148 | 149 | {-| -} 150 | listBase : R10.Theme.Theme -> List { color : Color.Color, name : String } 151 | listBase theme = 152 | R10.Color.Internal.Base.list theme 153 | 154 | 155 | {-| -} 156 | listDerived : R10.Theme.Theme -> List { color : Color.Color, name : String, description : String } 157 | listDerived theme = 158 | R10.Color.Internal.Derived.list theme 159 | 160 | 161 | {-| A sligtly modified version of `Color.Accessibility.maximumContrast` 162 | -} 163 | maximumContrast : Color.Color -> List Color.Color -> Maybe Color.Color 164 | maximumContrast = 165 | R10.Color.Internal.Derived.maximumContrast 166 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/Msg.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.Msg exposing 2 | ( Msg(..) 3 | , handleChangesSinceLastSubmissions 4 | , isChangingValues 5 | , isSubmitted 6 | , onLoseFocus 7 | , onOptionSelect 8 | , onValueChange 9 | , toValue 10 | ) 11 | 12 | import R10.Context 13 | import R10.Form.Internal.Conf 14 | import R10.Form.Internal.FieldConf 15 | import R10.Form.Internal.Key 16 | import R10.FormComponents.Internal.Phone.Common 17 | import R10.FormComponents.Internal.Single.Common 18 | 19 | 20 | toValue : Msg -> Maybe String 21 | toValue msg = 22 | case msg of 23 | ChangeValue _ _ _ _ string -> 24 | Just string 25 | 26 | _ -> 27 | Nothing 28 | 29 | 30 | {-| -} 31 | type Msg 32 | = NoOp 33 | | GetFocus R10.Form.Internal.Key.Key R10.Form.Internal.FieldConf.FieldConf 34 | | LoseFocus R10.Form.Internal.Key.Key R10.Form.Internal.FieldConf.FieldConf 35 | | Hover R10.Form.Internal.Key.Key (Maybe String) 36 | | TogglePasswordShow R10.Form.Internal.Key.Key 37 | | KeyDown Int 38 | | ChangeValue R10.Form.Internal.Key.Key R10.Form.Internal.FieldConf.FieldConf R10.Form.Internal.Conf.Conf R10.Context.ContextR10 String 39 | | ChangeTab R10.Form.Internal.Key.Key String 40 | | AddEntity R10.Form.Internal.Key.Key 41 | | RemoveEntity R10.Form.Internal.Key.Key 42 | | Submit R10.Form.Internal.Conf.Conf 43 | -- SUB COMPONENTS 44 | | OnSingleMsg R10.Form.Internal.Key.Key R10.Form.Internal.FieldConf.FieldConf R10.Form.Internal.Conf.Conf R10.FormComponents.Internal.Single.Common.Msg 45 | | OnPhoneMsg R10.Form.Internal.Key.Key R10.Form.Internal.FieldConf.FieldConf R10.Form.Internal.Conf.Conf R10.FormComponents.Internal.Phone.Common.Msg 46 | 47 | 48 | {-| -} 49 | isSubmitted : Msg -> Bool 50 | isSubmitted msgState = 51 | case msgState of 52 | Submit _ -> 53 | True 54 | 55 | _ -> 56 | False 57 | 58 | 59 | {-| -} 60 | handleChangesSinceLastSubmissions : Bool -> Msg -> Bool 61 | handleChangesSinceLastSubmissions changesSinceLastSubmissions msg = 62 | case msg of 63 | Submit _ -> 64 | -- We reset the value 65 | False 66 | 67 | _ -> 68 | if isChangingValues msg then 69 | True 70 | 71 | else 72 | changesSinceLastSubmissions 73 | 74 | 75 | {-| Return true if the message can potentially change some value 76 | -} 77 | isChangingValues : Msg -> Bool 78 | isChangingValues msg = 79 | case msg of 80 | ChangeValue _ _ _ _ _ -> 81 | True 82 | 83 | AddEntity _ -> 84 | True 85 | 86 | RemoveEntity _ -> 87 | True 88 | 89 | _ -> 90 | False 91 | 92 | 93 | onLoseFocus : 94 | (R10.Form.Internal.Key.Key 95 | -> R10.Form.Internal.FieldConf.FieldConf 96 | -> any 97 | ) 98 | -> Msg 99 | -> Maybe any 100 | onLoseFocus func msg_ = 101 | case msg_ of 102 | LoseFocus key conf -> 103 | Just (func key conf) 104 | 105 | _ -> 106 | Nothing 107 | 108 | 109 | onValueChange : 110 | (R10.Form.Internal.Key.Key 111 | -> R10.Form.Internal.FieldConf.FieldConf 112 | -> R10.Form.Internal.Conf.Conf 113 | -> String 114 | -> any 115 | ) 116 | -> Msg 117 | -> Maybe any 118 | onValueChange func msg_ = 119 | case msg_ of 120 | ChangeValue key fieldConf formConf _ value -> 121 | Just (func key fieldConf formConf value) 122 | 123 | _ -> 124 | Nothing 125 | 126 | 127 | onOptionSelect : 128 | (R10.Form.Internal.Key.Key 129 | -> R10.Form.Internal.FieldConf.FieldConf 130 | -> R10.Form.Internal.Conf.Conf 131 | -> String 132 | -> any 133 | ) 134 | -> Msg 135 | -> Maybe any 136 | onOptionSelect func msg_ = 137 | case msg_ of 138 | OnSingleMsg key fieldConf formConf singleMsg -> 139 | case singleMsg of 140 | R10.FormComponents.Internal.Single.Common.OnOptionSelect option -> 141 | Just (func key fieldConf formConf option) 142 | 143 | _ -> 144 | Nothing 145 | 146 | _ -> 147 | Nothing 148 | -------------------------------------------------------------------------------- /src/R10/Table/Internal/Placeholder.elm: -------------------------------------------------------------------------------- 1 | module R10.Table.Internal.Placeholder exposing (placeholderSvg, view) 2 | 3 | import Element.WithContext exposing (..) 4 | import Element.WithContext.Border as Border 5 | import R10.Context exposing (..) 6 | import Svg 7 | import Svg.Attributes as SA 8 | 9 | 10 | toRadix : Int -> String 11 | toRadix n = 12 | -- Internal Utils for getting radix number 13 | let 14 | getChr c = 15 | if c < 10 then 16 | String.fromInt c 17 | 18 | else 19 | String.fromChar <| Char.fromCode (87 + c) 20 | 21 | result = 22 | if n < 16 then 23 | getChr n 24 | 25 | else 26 | toRadix (n // 16) ++ getChr (remainderBy 16 n) 27 | in 28 | result 29 | 30 | 31 | toHex : Color -> String 32 | toHex color = 33 | let 34 | { red, green, blue } = 35 | toRgb color 36 | in 37 | [ red * 255, green * 255, blue * 255 ] 38 | |> List.map ceiling 39 | |> List.map (toRadix >> String.padLeft 2 '0') 40 | |> (::) "#" 41 | |> String.join "" 42 | 43 | 44 | placeholderSvg : Color -> Element (R10.Context.ContextInternal z) msg 45 | placeholderSvg color = 46 | let 47 | stringColor = 48 | toHex color 49 | 50 | fillId = 51 | "fill" ++ stringColor 52 | 53 | speed = 54 | "2s" 55 | in 56 | Element.WithContext.html <| 57 | Svg.svg 58 | [ SA.xmlSpace "http://www.w3.org/2000/svg" 59 | , SA.width "auto" 60 | , SA.height "auto" 61 | ] 62 | [ Svg.rect 63 | [ SA.fill "none" 64 | , SA.x "0" 65 | , SA.y "0" 66 | , SA.width "100%" 67 | , SA.height "100%" 68 | , SA.style <| "fill: url(#" ++ fillId ++ ");" 69 | ] 70 | [] 71 | , Svg.defs [] 72 | [ Svg.linearGradient 73 | [ SA.id fillId ] 74 | [ Svg.stop 75 | [ SA.offset "0.599964" 76 | , SA.stopColor stringColor 77 | , SA.stopOpacity "0.7" 78 | ] 79 | [ Svg.animate 80 | [ SA.attributeName "offset" 81 | , SA.values "-2; -2; 1" 82 | , SA.keyTimes "0; 0.25; 1" 83 | , SA.dur speed 84 | , SA.repeatCount "indefinite" 85 | ] 86 | [] 87 | ] 88 | , Svg.stop 89 | [ SA.offset "1.59996" 90 | , SA.stopColor stringColor 91 | , SA.stopOpacity "1" 92 | ] 93 | [ Svg.animate 94 | [ SA.attributeName "offset" 95 | , SA.values "-1; -1; 2" 96 | , SA.keyTimes "0; 0.25; 1" 97 | , SA.dur speed 98 | , SA.repeatCount "indefinite" 99 | ] 100 | [] 101 | ] 102 | , Svg.stop 103 | [ SA.offset "2.59996" 104 | , SA.stopColor stringColor 105 | , SA.stopOpacity "0.7" 106 | ] 107 | [ Svg.animate 108 | [ SA.attributeName "offset" 109 | , SA.values "0; 0; 3" 110 | , SA.keyTimes "0; 0.25; 1" 111 | , SA.dur speed 112 | , SA.repeatCount "indefinite" 113 | ] 114 | [] 115 | ] 116 | ] 117 | ] 118 | ] 119 | 120 | 121 | view : Color -> List (Attribute (R10.Context.ContextInternal z) msg) -> Element (R10.Context.ContextInternal z) msg 122 | view color attrs = 123 | el ([ width fill, height <| px 16, Border.rounded 4, clip, alpha 0.6 ] ++ attrs) <| 124 | (placeholderSvg <| color) 125 | -------------------------------------------------------------------------------- /examples/pwa/src/Pages/Form_Entities.elm: -------------------------------------------------------------------------------- 1 | module Pages.Form_Entities exposing 2 | ( Model 3 | , Msg 4 | , init 5 | , update 6 | , view 7 | ) 8 | 9 | import Element exposing (..) 10 | import Element.Background as Background 11 | import Element.Border as Border 12 | import Html.Attributes 13 | import Markdown 14 | import R10.Form 15 | import R10.FormTypes 16 | import R10.Paragraph 17 | import R10.Theme 18 | 19 | 20 | type alias Model = 21 | { formState : R10.Form.State } 22 | 23 | 24 | init : Model 25 | init = 26 | { formState = R10.Form.initState } 27 | 28 | 29 | type Msg 30 | = FormMsg R10.Form.Msg 31 | 32 | 33 | update : Msg -> Model -> Model 34 | update msg model = 35 | case msg of 36 | FormMsg formMsg -> 37 | let 38 | ( newFormState, _ ) = 39 | R10.Form.update formMsg model.formState 40 | in 41 | { model | formState = newFormState } 42 | 43 | 44 | 45 | -- 46 | 47 | 48 | inputFieldConf : Int -> R10.Form.Entity 49 | inputFieldConf id = 50 | R10.Form.entity.field 51 | { id = "text" ++ String.fromInt id 52 | , idDom = Nothing 53 | , type_ = R10.FormTypes.inputField.textPlain 54 | , label = "Text " ++ String.fromInt id 55 | , helperText = Nothing 56 | , requiredLabel = Nothing 57 | , validationSpecs = Nothing 58 | } 59 | 60 | 61 | entities : List R10.Form.Entity 62 | entities = 63 | [ inputFieldConf 1, inputFieldConf 2, inputFieldConf 3 ] 64 | 65 | 66 | entitiesForTabs : List ( String, R10.Form.Entity ) 67 | entitiesForTabs = 68 | [ ( "Tab 1", inputFieldConf 1 ), ( "Tab 2", inputFieldConf 2 ), ( "Tab 3", inputFieldConf 3 ) ] 69 | 70 | 71 | helperForTitles : { helperText : Maybe a1, title : String, validationSpecs : Maybe a } 72 | helperForTitles = 73 | { title = "Title", helperText = Nothing, validationSpecs = Nothing } 74 | 75 | 76 | section : String -> R10.Form.Form -> R10.Theme.Theme -> List (Element Msg) 77 | section entityAsString form theme = 78 | [ R10.Paragraph.xlarge [ Background.color <| rgba 1 1 0 0.5, padding 5 ] [ text entityAsString ] 79 | , row [ Border.width 5, Border.color <| rgba 0.95 0.95 0 1 ] <| R10.Form.viewWithTheme form FormMsg theme 80 | , row [ Border.width 5, Border.color <| rgba 0.95 0.95 0 1, width fill ] <| R10.Form.viewWithTheme form FormMsg theme 81 | ] 82 | 83 | 84 | view : Model -> R10.Theme.Theme -> List (Element Msg) 85 | view model theme = 86 | let 87 | state : R10.Form.State 88 | state = 89 | model.formState 90 | in 91 | -- 92 | -- Look at R10.FormComponents.Text.view line 385 93 | -- 94 | [] 95 | ++ [ paragraph [] [ html <| Markdown.toHtml [ Html.Attributes.class "markdown" ] """**Entities** are the constituent of the form configuration. 96 | 97 | Each **entity** can represent an input field, a title, a sub-title or a list of other entities. 98 | 99 | As list of other entities is used to represent some grouping. For example to add a border around certain number of input field that are somehow related. 100 | 101 | There are 8 types of entity: 102 | 103 | 1. R10.Form.entity.field 104 | 2. R10.Form.entity.normal 105 | 3. R10.Form.entity.wrappable 106 | 4. R10.Form.entity.withBorder 107 | 5. R10.Form.entity.withTabs 108 | 6. R10.Form.entity.multi 109 | 7. R10.Form.entity.title 110 | 8. R10.Form.entity.subTitle""" ] ] 111 | ++ section "R10.Form.entity.field" { conf = entities, state = state } theme 112 | ++ section "R10.Form.entity.normal" { conf = [ R10.Form.entity.normal "normal" entities ], state = state } theme 113 | ++ section "R10.Form.entity.wrappable" { conf = [ R10.Form.entity.wrappable "wrappable" entities ], state = state } theme 114 | ++ section "R10.Form.entity.withBorder" { conf = [ R10.Form.entity.withBorder "withBorder" entities ], state = state } theme 115 | ++ section "R10.Form.entity.withTabs" { conf = [ R10.Form.entity.withTabs "withTabs" entitiesForTabs ], state = state } theme 116 | ++ section "R10.Form.entity.multi" { conf = [ R10.Form.entity.multi "multi" entities ], state = state } theme 117 | ++ section "R10.Form.entity.title" { conf = [ R10.Form.entity.title "title" helperForTitles ], state = state } theme 118 | ++ section "R10.Form.entity.subTitle" { conf = [ R10.Form.entity.subTitle "subTitle" helperForTitles ], state = state } theme 119 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for considering contributing to R10! 4 | 5 | When contributing to this repository, please first discuss the change you wish to make via issue, 6 | email, or any other method with the owners of this repository before making a change. 7 | 8 | Please note we have a code of conduct, please follow it in all your interactions with the project. 9 | 10 | ## Pull Request Process 11 | 12 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 13 | build. 14 | 2. Update the README.md with details of changes to the interface, this includes new environment 15 | variables, exposed ports, useful file locations and container parameters. 16 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 17 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/) and 18 | it is enforced by the Elm Semantic Versioning system. 19 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 20 | do not have permission to do that, you may request the second reviewer to merge it for you. 21 | 22 | ## Code of Conduct 23 | 24 | ### Our Pledge 25 | 26 | In the interest of fostering an open and welcoming environment, we as 27 | contributors and maintainers pledge to making participation in our project and 28 | our community a harassment-free experience for everyone, regardless of age, body 29 | size, disability, ethnicity, gender identity and expression, level of experience, 30 | nationality, personal appearance, race, religion, or sexual identity and 31 | orientation. 32 | 33 | ### Our Standards 34 | 35 | Examples of behavior that contributes to creating a positive environment 36 | include: 37 | 38 | * Using welcoming and inclusive language 39 | * Being respectful of differing viewpoints and experiences 40 | * Gracefully accepting constructive criticism 41 | * Focusing on what is best for the community 42 | * Showing empathy towards other community members 43 | 44 | Examples of unacceptable behavior by participants include: 45 | 46 | * The use of sexualized language or imagery and unwelcome sexual attention or 47 | advances 48 | * Trolling, insulting/derogatory comments, and personal or political attacks 49 | * Public or private harassment 50 | * Publishing others' private information, such as a physical or electronic 51 | address, without explicit permission 52 | * Other conduct which could reasonably be considered inappropriate in a 53 | professional setting 54 | 55 | ### Our Responsibilities 56 | 57 | Project maintainers are responsible for clarifying the standards of acceptable 58 | behavior and are expected to take appropriate and fair corrective action in 59 | response to any instances of unacceptable behavior. 60 | 61 | Project maintainers have the right and responsibility to remove, edit, or 62 | reject comments, commits, code, wiki edits, issues, and other contributions 63 | that are not aligned to this Code of Conduct, or to ban temporarily or 64 | permanently any contributor for other behaviors that they deem inappropriate, 65 | threatening, offensive, or harmful. 66 | 67 | ### Scope 68 | 69 | This Code of Conduct applies both within project spaces and in public spaces 70 | when an individual is representing the project or its community. Examples of 71 | representing a project or community include using an official project e-mail 72 | address, posting via an official social media account, or acting as an appointed 73 | representative at an online or offline event. Representation of a project may be 74 | further defined and clarified by project maintainers. 75 | 76 | ### Enforcement 77 | 78 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 79 | reported by contacting the project team. All 80 | complaints will be reviewed and investigated and will result in a response that 81 | is deemed necessary and appropriate to the circumstances. The project team is 82 | obligated to maintain confidentiality with regard to the reporter of an incident. 83 | Further details of specific enforcement policies may be posted separately. 84 | 85 | Project maintainers who do not follow or enforce the Code of Conduct in good 86 | faith may face temporary or permanent repercussions as determined by other 87 | members of the project's leadership. 88 | 89 | ### Attribution 90 | 91 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 92 | available at [http://contributor-covenant.org/version/1/4][version] 93 | 94 | [homepage]: http://contributor-covenant.org 95 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/Single/Common.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.Single.Common exposing 2 | ( Args 3 | , FieldOption 4 | , Model 5 | , Msg(..) 6 | , dropdownContainerId 7 | , dropdownContentId 8 | , filterBySearch 9 | , getSelectedOrFirst 10 | , init 11 | , singleSearchBoxId 12 | ) 13 | 14 | import Element.WithContext exposing (..) 15 | import R10.Context exposing (..) 16 | import R10.FormComponents.Internal.Style 17 | import R10.FormTypes 18 | import R10.Palette 19 | 20 | 21 | 22 | -- types 23 | 24 | 25 | type Msg 26 | = NoOp 27 | | Hover (Maybe String) 28 | | OnFocus 29 | | OnLoseFocus String 30 | | OnScroll Float 31 | | OnEsc 32 | -- 33 | | OnInputClick { key : String, selectedY : Float } 34 | | OnOptionSelect String 35 | | OnSearch { key : String, selectOptionHeight : Int, maxDisplayCount : Int, filteredFieldOption : List FieldOption } String 36 | | OnArrowUp { key : String, selectOptionHeight : Int, maxDisplayCount : Int, filteredFieldOption : List FieldOption } 37 | | OnArrowDown { key : String, selectOptionHeight : Int, maxDisplayCount : Int, filteredFieldOption : List FieldOption } 38 | -- 39 | | OnDelBackspace 40 | 41 | 42 | init : Model 43 | init = 44 | { value = "" 45 | , search = "" 46 | , focused = False 47 | , scroll = 0 48 | , opened = False 49 | , select = "" 50 | , over = Nothing 51 | } 52 | 53 | 54 | type alias Model = 55 | { value : String 56 | , search : String 57 | , focused : Bool 58 | , scroll : Float 59 | , opened : Bool 60 | , select : String 61 | , over : Maybe String 62 | } 63 | 64 | 65 | type alias FieldOption = 66 | { value : String 67 | , label : String 68 | } 69 | 70 | 71 | type alias Args z msg = 72 | { ---- Messages 73 | toMsg : Msg -> msg 74 | 75 | -- Stuff that doesn't change 76 | , label : String 77 | , helperText : Maybe String 78 | , disabled : Bool 79 | , maybeValid : Maybe Bool 80 | , requiredLabel : Maybe String 81 | , style : R10.FormComponents.Internal.Style.Style 82 | , key : String 83 | , palette : R10.Palette.Palette 84 | , searchable : Bool 85 | , autocomplete : Maybe String 86 | 87 | -- Specific 88 | , singleType : R10.FormTypes.TypeSingle 89 | , fieldOptions : List FieldOption 90 | , viewOptionEl : FieldOption -> Element (R10.Context.ContextInternal z) msg 91 | , searchFn : String -> FieldOption -> Bool 92 | , selectOptionHeight : Int 93 | , maxDisplayCount : Int 94 | , leadingIcon : List (Element (R10.Context.ContextInternal z) msg) 95 | , trailingIcon : List (Element (R10.Context.ContextInternal z) msg) 96 | } 97 | 98 | 99 | isAnyOptionValueMatched : { a | value : String, fieldOptions : List FieldOption } -> Bool 100 | isAnyOptionValueMatched { value, fieldOptions } = 101 | List.any (\option -> option.value == value) fieldOptions 102 | 103 | 104 | isAnyOptionLabelMatched : { a | value : String, fieldOptions : List FieldOption } -> Bool 105 | isAnyOptionLabelMatched { value, fieldOptions } = 106 | List.any (\option -> option.label == value) fieldOptions 107 | 108 | 109 | getSelectedOrFirst : List FieldOption -> String -> String -> String 110 | getSelectedOrFirst fieldOptions value select = 111 | if not <| String.isEmpty select then 112 | select 113 | 114 | else if isAnyOptionValueMatched { value = value, fieldOptions = fieldOptions } then 115 | value 116 | 117 | else 118 | List.head fieldOptions 119 | |> Maybe.map .value 120 | |> Maybe.withDefault "" 121 | 122 | 123 | filterBySearch : 124 | String 125 | -> 126 | { a 127 | | searchFn : String -> FieldOption -> Bool 128 | , fieldOptions : List FieldOption 129 | } 130 | -> List FieldOption 131 | filterBySearch search { searchFn, fieldOptions } = 132 | if 133 | String.isEmpty search 134 | || isAnyOptionLabelMatched { value = search, fieldOptions = fieldOptions } 135 | then 136 | fieldOptions 137 | 138 | else 139 | fieldOptions 140 | |> List.filter (searchFn search) 141 | 142 | 143 | dropdownContainerId : String -> String 144 | dropdownContainerId key = 145 | "single-dropdown-container-" ++ key 146 | 147 | 148 | dropdownContentId : String -> String 149 | dropdownContentId key = 150 | "single-dropdown-content-" ++ key 151 | 152 | 153 | singleSearchBoxId : String -> String 154 | singleSearchBoxId key = 155 | "single-dropdown-search-" ++ key 156 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/MakerForValidationKeys.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.MakerForValidationKeys exposing (Outcome, maker) 2 | 3 | import R10.Form.Internal.Conf 4 | import R10.Form.Internal.Dict 5 | import R10.Form.Internal.FieldConf 6 | import R10.Form.Internal.Key 7 | import R10.Form.Internal.Shared 8 | import R10.FormTypes 9 | import Set 10 | 11 | 12 | 13 | -- ██████ ██ ██ ████████ ██████ ██████ ███ ███ ███████ 14 | -- ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ 15 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ █████ 16 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 17 | -- ██████ ██████ ██ ██████ ██████ ██ ██ ███████ 18 | 19 | 20 | type alias Outcome = 21 | ( R10.Form.Internal.Key.Key, R10.FormTypes.FieldType, Maybe R10.Form.Internal.FieldConf.ValidationSpecs ) 22 | 23 | 24 | 25 | -- ██ ██ ███████ ██ ██████ ███████ ██████ ███████ 26 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 27 | -- ███████ █████ ██ ██████ █████ ██████ ███████ 28 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ 29 | -- ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ 30 | 31 | 32 | viewEntityMulti : 33 | R10.Form.Internal.Key.Key 34 | -> R10.Form.Internal.Shared.Form 35 | -> List Outcome 36 | viewEntityMulti key form = 37 | let 38 | quantity : Int 39 | quantity = 40 | Maybe.withDefault 1 <| R10.Form.Internal.Dict.get key form.state.multiplicableQuantities 41 | in 42 | List.concat <| 43 | List.indexedMap 44 | (\index _ -> 45 | let 46 | newKey : R10.Form.Internal.Key.Key 47 | newKey = 48 | R10.Form.Internal.Key.composeKey key (String.fromInt index) 49 | 50 | removed : Bool 51 | removed = 52 | Set.member (R10.Form.Internal.Key.toString newKey) form.state.removed 53 | in 54 | if removed then 55 | [] 56 | 57 | else 58 | maker newKey form 59 | ) 60 | (List.repeat quantity ()) 61 | 62 | 63 | 64 | -- ███ ███ █████ ██ ██ ███████ ██████ 65 | -- ████ ████ ██ ██ ██ ██ ██ ██ ██ 66 | -- ██ ████ ██ ███████ █████ █████ ██████ 67 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 68 | -- ██ ██ ██ ██ ██ ██ ███████ ██ ██ 69 | 70 | 71 | maker : 72 | R10.Form.Internal.Key.Key 73 | -> R10.Form.Internal.Shared.Form 74 | -> List Outcome 75 | maker key form = 76 | -- 77 | -- This is recursive 78 | -- 79 | -- ┌─────> maker >─────┐ 80 | -- │ │ 81 | -- └───────────────────┘ 82 | -- 83 | List.concat <| 84 | List.map 85 | (\entity -> 86 | case entity of 87 | R10.Form.Internal.Conf.EntityWrappable id entities -> 88 | maker (R10.Form.Internal.Key.composeKey key id) { conf = entities, state = form.state } 89 | 90 | R10.Form.Internal.Conf.EntityWithBorder id entities -> 91 | maker (R10.Form.Internal.Key.composeKey key id) { conf = entities, state = form.state } 92 | 93 | R10.Form.Internal.Conf.EntityNormal id entities -> 94 | maker (R10.Form.Internal.Key.composeKey key id) { conf = entities, state = form.state } 95 | 96 | R10.Form.Internal.Conf.EntityWithTabs id titleEntityList -> 97 | maker (R10.Form.Internal.Key.composeKey key id) { conf = titleEntityList |> List.map Tuple.second, state = form.state } 98 | 99 | R10.Form.Internal.Conf.EntityMulti entityId entities -> 100 | viewEntityMulti (R10.Form.Internal.Key.composeKey key entityId) { conf = entities, state = form.state } 101 | 102 | R10.Form.Internal.Conf.EntityField fieldConf -> 103 | [ ( R10.Form.Internal.Key.composeKey key fieldConf.id, fieldConf.type_, fieldConf.validationSpecs ) ] 104 | 105 | R10.Form.Internal.Conf.EntityTitle entityId textConf -> 106 | [ ( R10.Form.Internal.Key.composeKey key entityId, R10.FormTypes.TypeText R10.FormTypes.TextPlain, textConf.validationSpecs ) ] 107 | 108 | R10.Form.Internal.Conf.EntitySubTitle entityId textConf -> 109 | [ ( R10.Form.Internal.Key.composeKey key entityId, R10.FormTypes.TypeText R10.FormTypes.TextPlain, textConf.validationSpecs ) ] 110 | ) 111 | form.conf 112 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/State.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.State exposing 2 | ( State 3 | , encoder 4 | , fromString 5 | , init 6 | , toString 7 | ) 8 | 9 | import Dict 10 | import Json.Decode as D 11 | import Json.Decode.Extra 12 | import Json.Decode.Pipeline 13 | import Json.Encode as E 14 | import Json.Encode.Extra 15 | import R10.Form.Internal.FieldState 16 | import R10.Form.Internal.Key 17 | import R10.Form.Internal.QtySubmitAttempted 18 | import Set 19 | 20 | 21 | 22 | -- ████████ ██ ██ ██████ ███████ ███████ 23 | -- ██ ██ ██ ██ ██ ██ ██ 24 | -- ██ ████ ██████ █████ ███████ 25 | -- ██ ██ ██ ██ ██ 26 | -- ██ ██ ██ ███████ ███████ 27 | -- 28 | 29 | 30 | type alias State = 31 | { fieldsState : Dict.Dict R10.Form.Internal.Key.KeyAsString R10.Form.Internal.FieldState.FieldState 32 | , multiplicableQuantities : Dict.Dict R10.Form.Internal.Key.KeyAsString Int 33 | , activeTabs : Dict.Dict R10.Form.Internal.Key.KeyAsString String 34 | , focused : Maybe R10.Form.Internal.Key.KeyAsString 35 | , active : Maybe R10.Form.Internal.Key.KeyAsString 36 | , removed : Set.Set R10.Form.Internal.Key.KeyAsString 37 | , qtySubmitAttempted : R10.Form.Internal.QtySubmitAttempted.QtySubmitAttempted 38 | , changesSinceLastSubmissions : Bool 39 | , lastKeyDownIsProcess : Bool 40 | } 41 | 42 | 43 | 44 | -- ██ ███ ██ ██ ████████ 45 | -- ██ ████ ██ ██ ██ 46 | -- ██ ██ ██ ██ ██ ██ 47 | -- ██ ██ ██ ██ ██ ██ 48 | -- ██ ██ ████ ██ ██ 49 | 50 | 51 | init : State 52 | init = 53 | { fieldsState = Dict.empty 54 | , multiplicableQuantities = Dict.empty 55 | , activeTabs = Dict.empty 56 | , focused = Nothing 57 | , active = Nothing 58 | , removed = Set.empty 59 | , qtySubmitAttempted = R10.Form.Internal.QtySubmitAttempted.fromInt 0 60 | , changesSinceLastSubmissions = False 61 | , lastKeyDownIsProcess = False 62 | } 63 | 64 | 65 | 66 | -- 67 | -- ███████ ███ ██ ██████ ██████ ██████ ███████ ██████ 68 | -- ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ 69 | -- █████ ██ ██ ██ ██ ██ ██ ██ ██ █████ ██████ 70 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 71 | -- ███████ ██ ████ ██████ ██████ ██████ ███████ ██ ██ 72 | -- 73 | -- ██████ ███████ ██████ ██████ ██████ ███████ ██████ 74 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 75 | -- ██ ██ █████ ██ ██ ██ ██ ██ █████ ██████ 76 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 77 | -- ██████ ███████ ██████ ██████ ██████ ███████ ██ ██ 78 | 79 | 80 | encoder : State -> E.Value 81 | encoder v = 82 | E.object 83 | [ ( "fieldsState", R10.Form.Internal.FieldState.encoderFieldState v.fieldsState ) 84 | , ( "multiplicableQuantities", E.dict identity E.int v.multiplicableQuantities ) 85 | , ( "activeTabs", E.dict identity E.string v.activeTabs ) 86 | , ( "focused", Json.Encode.Extra.maybe E.string v.focused ) 87 | 88 | -- We don't want to save active state since it can cause incorrect render on load 89 | , ( "active", Json.Encode.Extra.maybe E.string Nothing ) 90 | , ( "removed", E.list E.string (Set.toList v.removed) ) 91 | , ( "qtySubmitAttempted", E.int (R10.Form.Internal.QtySubmitAttempted.toInt v.qtySubmitAttempted) ) 92 | , ( "changesSinceLastSubmissions", E.bool v.changesSinceLastSubmissions ) 93 | , ( "lastKeyDownIsProcess", E.bool v.lastKeyDownIsProcess ) 94 | ] 95 | 96 | 97 | decoder : D.Decoder State 98 | decoder = 99 | D.succeed State 100 | |> Json.Decode.Pipeline.required "fieldsState" R10.Form.Internal.FieldState.decoderFieldState 101 | |> Json.Decode.Pipeline.required "multiplicableQuantities" (D.dict D.int) 102 | |> Json.Decode.Pipeline.required "activeTabs" (D.dict D.string) 103 | |> Json.Decode.Pipeline.required "focused" (D.nullable D.string) 104 | |> Json.Decode.Pipeline.required "active" (D.nullable D.string) 105 | |> Json.Decode.Pipeline.required "removed" (Json.Decode.Extra.set D.string) 106 | |> Json.Decode.Pipeline.required "qtySubmitAttempted" (D.map R10.Form.Internal.QtySubmitAttempted.fromInt D.int) 107 | |> Json.Decode.Pipeline.required "changesSinceLastSubmissions" D.bool 108 | |> Json.Decode.Pipeline.required "lastKeyDownIsProcess" D.bool 109 | 110 | 111 | toString : State -> String 112 | toString v = 113 | E.encode 4 <| encoder v 114 | 115 | 116 | fromString : String -> Result D.Error State 117 | fromString string = 118 | D.decodeString decoder string 119 | -------------------------------------------------------------------------------- /src/R10/FormDebug.elm: -------------------------------------------------------------------------------- 1 | module R10.FormDebug exposing (textTypeMetaData, binaryTypeMetaData, singleTypeMetaData) 2 | 3 | {-| Only used for form debugging 4 | 5 | @docs textTypeMetaData, binaryTypeMetaData, singleTypeMetaData 6 | 7 | -} 8 | 9 | import R10.FormTypes 10 | 11 | 12 | {-| -} 13 | textTypeMetaData : 14 | R10.FormTypes.TypeText 15 | -> { next : R10.FormTypes.TypeText, string : String } 16 | textTypeMetaData textType = 17 | case textType of 18 | R10.FormTypes.TextPasswordNew _ -> 19 | { string = "R10.FormTypes.TextPasswordNew" 20 | , next = R10.FormTypes.TextPasswordCurrent "" 21 | } 22 | 23 | R10.FormTypes.TextPasswordCurrent _ -> 24 | { string = "R10.FormTypes.TextPasswordCurrent" 25 | , next = R10.FormTypes.TextPlain 26 | } 27 | 28 | R10.FormTypes.TextPlain -> 29 | { string = "R10.FormTypes.TextPlain" 30 | , next = R10.FormTypes.TextEmail 31 | } 32 | 33 | R10.FormTypes.TextEmail -> 34 | { string = "R10.FormTypes.TextEmail" 35 | , next = R10.FormTypes.TextUsername 36 | } 37 | 38 | R10.FormTypes.TextUsername -> 39 | { string = "R10.FormTypes.TextUsername" 40 | , next = R10.FormTypes.TextMultiline 41 | } 42 | 43 | R10.FormTypes.TextMultiline -> 44 | { string = "R10.FormTypes.TextMultiline" 45 | , next = R10.FormTypes.TextWithPattern "YY/MMMM" 46 | } 47 | 48 | R10.FormTypes.TextWithPattern pattern -> 49 | { string = "R10.FormTypes.TextWithPattern " ++ pattern 50 | , next = R10.FormTypes.TextEmailWithSuggestions [ "google.com", "hotmail.com", "yahoo.com" ] 51 | } 52 | 53 | R10.FormTypes.TextEmailWithSuggestions suggestions -> 54 | { string = "R10.FormTypes.TextEmailWithSuggestions [ " ++ String.join ", " suggestions ++ " ]" 55 | , next = R10.FormTypes.TextMobileEmail 56 | } 57 | 58 | R10.FormTypes.TextMobileEmail -> 59 | { string = "R10.FormTypes.TextMobileEmail" 60 | , next = R10.FormTypes.TextUsernameWithUseEmailCheckbox "" 61 | } 62 | 63 | R10.FormTypes.TextUsernameWithUseEmailCheckbox data -> 64 | { string = "R10.FormTypes.TextUsernameWithUseEmailCheckbox " ++ data 65 | , next = R10.FormTypes.TextWithPatternLarge "" 66 | } 67 | 68 | R10.FormTypes.TextWithPatternLarge data -> 69 | { string = "R10.FormTypes.TextWithPatternLarge " ++ data 70 | , next = R10.FormTypes.TextWithPatternLargeWithoutLabel "" 71 | } 72 | 73 | R10.FormTypes.TextWithPatternLargeWithoutLabel data -> 74 | { string = "R10.FormTypes.TextWithPatternLargeWithoutLabel " ++ data 75 | , next = R10.FormTypes.TextOnlyDigitsOrDash 76 | } 77 | 78 | R10.FormTypes.TextOnlyDigitsOrDash -> 79 | { string = "R10.FormTypes.TextOnlyDigitsOrDash" 80 | , next = R10.FormTypes.TextPasswordNew "" 81 | } 82 | 83 | 84 | {-| -} 85 | binaryTypeMetaData : 86 | R10.FormTypes.TypeBinary 87 | -> { next : R10.FormTypes.TypeBinary, string : String } 88 | binaryTypeMetaData textType = 89 | case textType of 90 | R10.FormTypes.BinaryCheckbox -> 91 | { string = "R10.FormTypes.BinaryCheckbox" 92 | , next = R10.FormTypes.BinarySwitch 93 | } 94 | 95 | R10.FormTypes.BinarySwitch -> 96 | { string = "R10.FormTypes.BinarySwitch" 97 | , next = R10.FormTypes.BinaryCheckbox 98 | } 99 | 100 | 101 | {-| -} 102 | singleTypeMetaData : 103 | R10.FormTypes.TypeSingle 104 | -> { next : R10.FormTypes.TypeSingle, string : String } 105 | singleTypeMetaData singleType = 106 | case singleType of 107 | R10.FormTypes.SingleRadio -> 108 | { string = "R10.FormTypes.SingleRadio" 109 | , next = R10.FormTypes.SingleRadioRow 110 | } 111 | 112 | R10.FormTypes.SingleRadioRow -> 113 | { string = "R10.FormTypes.SingleRadio" 114 | , next = R10.FormTypes.SingleCombobox 115 | } 116 | 117 | R10.FormTypes.SingleCombobox -> 118 | { string = "R10.FormTypes.SingleCombobox" 119 | , next = R10.FormTypes.SingleSelect 120 | } 121 | 122 | R10.FormTypes.SingleSelect -> 123 | { string = "R10.FormTypes.SingleSelect" 124 | , next = R10.FormTypes.SingleComboboxForCountry 125 | } 126 | 127 | R10.FormTypes.SingleComboboxForCountry -> 128 | { string = "R10.FormTypes.SingleComboboxForCountry" 129 | , next = R10.FormTypes.SingleRadio 130 | } 131 | -------------------------------------------------------------------------------- /examples/pwa/src-elm-starter/Starter/SnippetJavascript.elm: -------------------------------------------------------------------------------- 1 | module Starter.SnippetJavascript exposing 2 | ( appWorkAlsoWithoutJS 3 | , metaInfo 4 | , metaInfoData 5 | , portChangeMeta 6 | , portOnUrlChange 7 | , portPushUrl 8 | , registerServiceWorker 9 | , selfInvoking 10 | , signature 11 | ) 12 | 13 | import Json.Encode 14 | import Starter.Flags 15 | 16 | 17 | selfInvoking : String -> String 18 | selfInvoking code = 19 | "( function () {\"use strict\";\n" ++ code ++ "\n})();" 20 | 21 | 22 | metaInfo : Starter.Flags.Flags -> String 23 | metaInfo flags = 24 | "window.ElmStarter = " ++ metaInfoData flags ++ ";" 25 | 26 | 27 | metaInfoData : Starter.Flags.Flags -> String 28 | metaInfoData flags = 29 | Json.Encode.encode 4 <| 30 | Starter.Flags.encoder flags 31 | 32 | 33 | signature : String 34 | signature = 35 | selfInvoking <| """ 36 | var color = 37 | { default: "background: #eee; color: gray; font-family: monospace" 38 | , love: "background: red; color: #eee" 39 | , elm: "background: #77d7ef; color: #00479a" 40 | }; 41 | var emptyLine = " ".repeat(49); 42 | var message = 43 | [ "" 44 | , "%c" 45 | , emptyLine 46 | , " m a d e w i t h %c ❤ %c a n d %c e l m %c " 47 | , emptyLine 48 | , "" 49 | , "" 50 | ].join("\\n"); 51 | console.info 52 | ( message 53 | , color.default 54 | , color.love 55 | , color.default 56 | , color.elm 57 | , color.default 58 | );""" 59 | 60 | 61 | registerServiceWorker : String -> String 62 | registerServiceWorker relative = 63 | selfInvoking <| """ 64 | // From https://developers.google.com/web/tools/workbox/guides/get-started 65 | if (location.hostname === "localhost") { 66 | console.log("NOT loading the service worker in development"); 67 | } else { 68 | if ('serviceWorker' in navigator) { 69 | // Use the window load event to keep the page load performant 70 | window.addEventListener('load', function() { 71 | navigator.serviceWorker.register('""" ++ relative ++ """/service-worker.js').then(function(registration) { 72 | // Registration was successful 73 | }, function(err) { 74 | // registration failed :( 75 | }); 76 | }); 77 | } 78 | }""" 79 | 80 | 81 | {-| Changing "You need js..." to "Better to use js..." because 82 | the app is working also wihtout js in production when 83 | these pages are generated with Puppeteer 84 | -} 85 | appWorkAlsoWithoutJS : 86 | { a 87 | | messageEnableJavascriptForBetterExperience : String 88 | , messageYouNeedToEnableJavascript : String 89 | } 90 | -> String 91 | appWorkAlsoWithoutJS args = 92 | """ 93 | var noscriptElement = document.querySelector('noscript'); 94 | if (noscriptElement) { 95 | noscriptElement.innerHTML = noscriptElement.innerHTML.replace 96 | ( \"""" ++ args.messageYouNeedToEnableJavascript ++ """" 97 | , \"""" ++ args.messageEnableJavascriptForBetterExperience ++ """" 98 | ); 99 | } """ 100 | 101 | 102 | portOnUrlChange : String 103 | portOnUrlChange = 104 | """ 105 | // From https://github.com/elm/browser/blob/1.0.2/notes/navigation-in-elements.md 106 | // Inform app of browser navigation (the BACK and FORWARD buttons) 107 | if (ElmApp && ElmApp.ports && ElmApp.ports.onUrlChange) { 108 | window.addEventListener('popstate', function () { 109 | ElmApp.ports.onUrlChange.send(location.href); 110 | }); 111 | } """ 112 | 113 | 114 | portPushUrl : String 115 | portPushUrl = 116 | """ 117 | // From https://github.com/elm/browser/blob/1.0.2/notes/navigation-in-elements.md 118 | // Change the URL upon request, inform app of the change. 119 | if (ElmApp && ElmApp.ports && ElmApp.ports.pushUrl) { 120 | ElmApp.ports.pushUrl.subscribe(function(url) { 121 | history.pushState({}, '', url); 122 | window.scrollTo(0, 0); 123 | if (ElmApp && ElmApp.ports && ElmApp.ports.onUrlChange) { 124 | ElmApp.ports.onUrlChange.send(location.href); 125 | } 126 | }); 127 | } """ 128 | 129 | 130 | portChangeMeta : String 131 | portChangeMeta = 132 | """ 133 | if (ElmApp && ElmApp.ports && ElmApp.ports.changeMeta) { 134 | ElmApp.ports.changeMeta.subscribe(function(args) { 135 | if (args.querySelector !== "") { 136 | var element = document.querySelector(args.querySelector); 137 | if (element) { 138 | if (args.type_ == "attribute") { 139 | element.setAttribute(args.fieldName, args.content); 140 | } else if (args.type_ == "property" && element[args.fieldName]) { 141 | element[args.fieldName] = args.content; 142 | } 143 | } 144 | } 145 | }); 146 | } """ 147 | -------------------------------------------------------------------------------- /src/R10/DropDown.elm: -------------------------------------------------------------------------------- 1 | module R10.DropDown exposing (Option, extraCss, view, viewBorderLess) 2 | 3 | {-| Craate a Dropdown using HTML `select` 4 | 5 | @docs Option, extraCss, view, viewBorderLess 6 | 7 | -} 8 | 9 | import Element.WithContext exposing (..) 10 | import Html 11 | import Html.Attributes 12 | import Html.Events 13 | import Json.Decode 14 | import R10.Color.Utils 15 | import R10.Context exposing (..) 16 | import R10.Transition 17 | 18 | 19 | {-| -} 20 | type alias Option = 21 | { value : String 22 | , text : String 23 | } 24 | 25 | 26 | type Type 27 | = BorderLess 28 | 29 | 30 | renderHtmlOption : String -> Option -> Html.Html msg 31 | renderHtmlOption selected { value, text } = 32 | Html.option 33 | [ Html.Attributes.value value 34 | , Html.Attributes.selected (value == selected) 35 | ] 36 | [ Html.text text ] 37 | 38 | 39 | commonStyle : Float -> Int -> Color -> Color -> List (Html.Attribute msg) 40 | commonStyle ratio fontSize colorFont colorBackground = 41 | [ Html.Attributes.style "font-size" (String.fromInt fontSize ++ "px") 42 | , Html.Attributes.style "padding" "8px 28px 8px 30px" 43 | , Html.Attributes.style "color" (R10.Color.Utils.toCssRgba colorFont) 44 | , Html.Attributes.style "background-color" (R10.Color.Utils.toCssRgba colorBackground) 45 | , Html.Attributes.style "-webkit-appearance" "none" 46 | , Html.Attributes.style "-moz-appearance" "none" 47 | , Html.Attributes.style "border-radius" "5px" 48 | , Html.Attributes.style "cursor" "pointer" 49 | , Html.Attributes.style "outline" "none" 50 | , Html.Attributes.class "drop-down" 51 | , Html.Attributes.style "transition" (R10.Transition.parseCharacteristics ratio "color .2s ease-out, background-color .2s ease-out") 52 | ] 53 | 54 | 55 | getDropDownStyle : Float -> Int -> Color -> Color -> Type -> List (Html.Attribute msg) 56 | getDropDownStyle ratio fontSize colorFont colorBackground dropDownType = 57 | case dropDownType of 58 | BorderLess -> 59 | -- Set margin left for alignment by compensating the spacing gap of padding left 60 | commonStyle ratio fontSize colorFont colorBackground 61 | ++ [ Html.Attributes.style "border" "none" 62 | , Html.Attributes.style "margin-left" "-8px" 63 | ] 64 | 65 | 66 | {-| The dropdown. 67 | -} 68 | view : 69 | List (Attribute (R10.Context.ContextInternal z) msg) 70 | -> 71 | { a 72 | | colorBackground : Color 73 | , colorFont : Color 74 | , currentValue : String 75 | , inputHandler : String -> msg 76 | , optionList : List Option 77 | , fontSize : Int 78 | } 79 | -> Type 80 | -> Element (R10.Context.ContextInternal z) msg 81 | view attrs args dropDownType = 82 | withContext <| 83 | \c -> 84 | el 85 | attrs 86 | (html <| 87 | Html.select 88 | ([ Html.Events.on "change" (Json.Decode.map args.inputHandler Html.Events.targetValue) 89 | , Html.Attributes.value args.currentValue 90 | ] 91 | ++ getDropDownStyle c.contextR10.debugger_transitionSpeed args.fontSize args.colorFont args.colorBackground dropDownType 92 | ) 93 | (List.map (renderHtmlOption args.currentValue) args.optionList) 94 | ) 95 | 96 | 97 | 98 | -- Public interface 99 | -- Facade based on `view` to construct different appearance of drop down components. 100 | 101 | 102 | {-| Slightly different version of the dropdown that has no borders. 103 | -} 104 | viewBorderLess : 105 | List (Attribute (R10.Context.ContextInternal z) msg) 106 | -> 107 | { a 108 | | colorBackground : Color 109 | , colorFont : Color 110 | , currentValue : String 111 | , inputHandler : String -> msg 112 | , optionList : List Option 113 | , fontSize : Int 114 | } 115 | -> Element (R10.Context.ContextInternal z) msg 116 | viewBorderLess attrs args = 117 | view attrs args BorderLess 118 | 119 | 120 | 121 | -- This is some extra CSS that you need to add to the page 122 | -- if you use this module. The String argument is the `colorHover` 123 | 124 | 125 | {-| -} 126 | extraCss : Color -> String 127 | extraCss colorHover = 128 | -- Remove the triangle button (select arrow) from IE11 129 | -- https://stackoverflow.com/questions/20163079/remove-select-arrow-on-ie 130 | """ 131 | select::-ms-expand { 132 | display: none; 133 | } 134 | .drop-down:hover, .drop-down:focus { 135 | background-color: """ ++ R10.Color.Utils.toCssRgba colorHover ++ """ !important; 136 | } 137 | """ 138 | -------------------------------------------------------------------------------- /src/R10/Form/Internal/MakerForValues.elm: -------------------------------------------------------------------------------- 1 | module R10.Form.Internal.MakerForValues exposing 2 | ( Outcome 3 | , maker 4 | , viewEntityMulti 5 | ) 6 | 7 | import R10.Form.Internal.Conf 8 | import R10.Form.Internal.Dict 9 | import R10.Form.Internal.FieldState 10 | import R10.Form.Internal.Key 11 | import R10.Form.Internal.Shared 12 | import R10.Form.Internal.State 13 | import R10.Form.Internal.StateForValues 14 | import Set 15 | 16 | 17 | 18 | -- ██████ ██ ██ ████████ ██████ ██████ ███ ███ ███████ 19 | -- ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ 20 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ █████ 21 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 22 | -- ██████ ██████ ██ ██████ ██████ ██ ██ ███████ 23 | 24 | 25 | type alias Outcome = 26 | R10.Form.Internal.StateForValues.Entity 27 | 28 | 29 | 30 | -- ██ ██ ███████ ██ ██████ ███████ ██████ ███████ 31 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 32 | -- ███████ █████ ██ ██████ █████ ██████ ███████ 33 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ 34 | -- ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ 35 | 36 | 37 | viewEntityMulti : 38 | R10.Form.Internal.Key.Key 39 | -> R10.Form.Internal.State.State 40 | -> List R10.Form.Internal.Conf.Entity 41 | -> List Outcome 42 | viewEntityMulti key formState entities = 43 | let 44 | quantity : Int 45 | quantity = 46 | Maybe.withDefault 1 <| R10.Form.Internal.Dict.get key formState.multiplicableQuantities 47 | in 48 | List.concat <| 49 | List.indexedMap 50 | (\index _ -> 51 | let 52 | newKey : R10.Form.Internal.Key.Key 53 | newKey = 54 | R10.Form.Internal.Key.composeKey key (String.fromInt index) 55 | 56 | removed : Bool 57 | removed = 58 | Set.member (R10.Form.Internal.Key.toString newKey) formState.removed 59 | in 60 | if removed then 61 | [] 62 | 63 | else 64 | [ R10.Form.Internal.StateForValues.EntityIndex index <| maker { state = formState, conf = entities } newKey ] 65 | ) 66 | (List.repeat quantity ()) 67 | 68 | 69 | 70 | -- ███ ███ █████ ██ ██ ███████ ██████ 71 | -- ████ ████ ██ ██ ██ ██ ██ ██ ██ 72 | -- ██ ████ ██ ███████ █████ █████ ██████ 73 | -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 74 | -- ██ ██ ██ ██ ██ ██ ███████ ██ ██ 75 | 76 | 77 | maker : 78 | R10.Form.Internal.Shared.Form 79 | -> R10.Form.Internal.Key.Key 80 | -> List Outcome 81 | maker form key = 82 | -- 83 | -- This is recursive 84 | -- 85 | -- ┌─────> maker >─────┐ 86 | -- │ │ 87 | -- └───────────────────┘ 88 | -- 89 | List.concat <| 90 | List.map 91 | (\entity -> 92 | case entity of 93 | R10.Form.Internal.Conf.EntityWrappable _ entities -> 94 | maker { form | conf = entities } key 95 | 96 | R10.Form.Internal.Conf.EntityWithBorder _ entities -> 97 | maker { form | conf = entities } key 98 | 99 | R10.Form.Internal.Conf.EntityNormal _ entities -> 100 | maker { form | conf = entities } key 101 | 102 | R10.Form.Internal.Conf.EntityWithTabs _ titleEntityList -> 103 | maker { form | conf = titleEntityList |> List.map Tuple.second } key 104 | 105 | R10.Form.Internal.Conf.EntityMulti key_ entities -> 106 | [ R10.Form.Internal.StateForValues.EntityMulti key_ <| viewEntityMulti key form.state entities ] 107 | 108 | R10.Form.Internal.Conf.EntityField fieldConf -> 109 | let 110 | newKey : R10.Form.Internal.Key.Key 111 | newKey = 112 | R10.Form.Internal.Key.composeKey key fieldConf.id 113 | 114 | fieldState : R10.Form.Internal.FieldState.FieldState 115 | fieldState = 116 | Maybe.withDefault R10.Form.Internal.FieldState.init <| R10.Form.Internal.Dict.get newKey form.state.fieldsState 117 | in 118 | [ R10.Form.Internal.StateForValues.EntityField fieldConf.id fieldState.value ] 119 | 120 | R10.Form.Internal.Conf.EntityTitle _ _ -> 121 | [] 122 | 123 | R10.Form.Internal.Conf.EntitySubTitle _ _ -> 124 | [] 125 | ) 126 | form.conf 127 | -------------------------------------------------------------------------------- /src/R10/Color/AttrsBorder.elm: -------------------------------------------------------------------------------- 1 | module R10.Color.AttrsBorder exposing (buttonSecondary, inputFieldCheckboxNormal, inputFieldCheckboxOver, inputFieldCheckboxSelected, inputFieldError, inputFieldFocused, inputFieldNormal, inputFieldSuccess, normal, shadow) 2 | 3 | {-| Border colors 4 | 5 | @docs buttonSecondary, inputFieldCheckboxNormal, inputFieldCheckboxOver, inputFieldCheckboxSelected, inputFieldError, inputFieldFocused, inputFieldNormal, inputFieldSuccess, normal, shadow 6 | 7 | -} 8 | 9 | import Element.WithContext exposing (..) 10 | import Element.WithContext.Border as Border 11 | import R10.Color.Internal.Derived 12 | import R10.Color.Utils 13 | import R10.Context 14 | 15 | 16 | 17 | -- NORMAL 18 | 19 | 20 | {-| -} 21 | normal : Attribute (R10.Context.ContextInternal z) msg 22 | normal = 23 | withContextAttribute <| 24 | \c -> 25 | R10.Color.Internal.Derived.Border 26 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 27 | |> R10.Color.Utils.fromColorColor 28 | |> Border.color 29 | 30 | 31 | 32 | -- SHADOW 33 | 34 | 35 | {-| -} 36 | shadow : { offset : ( Float, Float ), size : Float, blur : Float } -> Attribute (R10.Context.ContextInternal z) msg 37 | shadow { offset, size, blur } = 38 | withContextAttribute <| 39 | \c -> 40 | R10.Color.Internal.Derived.Border 41 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 42 | |> R10.Color.Utils.fromColorColor 43 | |> (\color -> { offset = offset, size = size, blur = blur, color = color }) 44 | |> Border.shadow 45 | 46 | 47 | 48 | -- BUTTON SECONDARY 49 | 50 | 51 | {-| -} 52 | buttonSecondary : Attribute (R10.Context.ContextInternal z) msg 53 | buttonSecondary = 54 | withContextAttribute <| 55 | \c -> 56 | R10.Color.Internal.Derived.Border 57 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 58 | |> R10.Color.Utils.fromColorColor 59 | |> Border.color 60 | 61 | 62 | 63 | -- INPUT FIELD 64 | 65 | 66 | {-| -} 67 | inputFieldNormal : Attribute (R10.Context.ContextInternal z) msg 68 | inputFieldNormal = 69 | withContextAttribute <| 70 | \c -> 71 | R10.Color.Internal.Derived.Border 72 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 73 | |> R10.Color.Utils.fromColorColor 74 | |> Border.color 75 | 76 | 77 | {-| -} 78 | inputFieldFocused : Attribute (R10.Context.ContextInternal z) msg 79 | inputFieldFocused = 80 | withContextAttribute <| 81 | \c -> 82 | R10.Color.Internal.Derived.Primary 83 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 84 | |> R10.Color.Utils.fromColorColor 85 | |> Border.color 86 | 87 | 88 | {-| -} 89 | inputFieldError : Attribute (R10.Context.ContextInternal z) msg 90 | inputFieldError = 91 | withContextAttribute <| 92 | \c -> 93 | R10.Color.Internal.Derived.FontAlertDanger 94 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 95 | |> R10.Color.Utils.fromColorColor 96 | |> Border.color 97 | 98 | 99 | {-| -} 100 | inputFieldSuccess : Attribute (R10.Context.ContextInternal z) msg 101 | inputFieldSuccess = 102 | withContextAttribute <| 103 | \c -> 104 | R10.Color.Internal.Derived.FontAlertSuccess 105 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 106 | |> R10.Color.Utils.fromColorColor 107 | |> Border.color 108 | 109 | 110 | 111 | -- CHECKBOX 112 | 113 | 114 | {-| -} 115 | inputFieldCheckboxNormal : Attribute (R10.Context.ContextInternal z) msg 116 | inputFieldCheckboxNormal = 117 | withContextAttribute <| 118 | \c -> 119 | R10.Color.Internal.Derived.Border 120 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 121 | |> R10.Color.Utils.fromColorColor 122 | |> Border.color 123 | 124 | 125 | {-| -} 126 | inputFieldCheckboxSelected : Attribute (R10.Context.ContextInternal z) msg 127 | inputFieldCheckboxSelected = 128 | withContextAttribute <| 129 | \c -> 130 | R10.Color.Internal.Derived.Primary 131 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 132 | |> R10.Color.Utils.fromColorColor 133 | |> Border.color 134 | 135 | 136 | {-| -} 137 | inputFieldCheckboxOver : Decoration (R10.Context.ContextInternal z) 138 | inputFieldCheckboxOver = 139 | withContextDecoration <| 140 | \c -> 141 | R10.Color.Internal.Derived.Primary 142 | |> R10.Color.Internal.Derived.toColor c.contextR10.theme 143 | |> R10.Color.Utils.fromColorColor 144 | |> Border.color 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R10 2 | 3 | ![R10 logo](https://r10.netlify.app/images/r10.png) 4 | 5 | **R10** is a library of interactive building blocks written in [Elm](https://elm-lang.org/) and [elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/) that we use at [Rakuten](https://global.rakuten.com/) for creating user interfaces. 6 | 7 | **Disclaimer**: This library is actively used in our live projects and the code and the documentation can be rough in most places because, you know, deadlines! It is also tailored for our use so it is probably useful as source of information rather than as a real dependency to add in your projects. 8 | 9 | ### Links 10 | 11 | * [R10 demo with documentation and examples](https://r10.netlify.app/) 12 | * [R10 in the Elm's package website](https://package.elm-lang.org/packages/rakutentech/r10/latest/) 13 | * [R10 in Github](https://github.com/rakutentech/r10) 14 | * [Ellie: R10 Simple View](https://ellie-app.com/j3Xg2HZ4V62a1) 15 | * [Ellie: R10 Simple Form](https://ellie-app.com/j3Xj8dmJzvja1) 16 | * [Ellie: R10 Credit Card Form](https://ellie-app.com/j3Xk8ZqvKGYa1) 17 | 18 | # How to use the R10 library 19 | 20 | If you already have an existing Elm project, install the library with 21 | 22 | ```bash 23 | elm install rakutentech/r10 24 | ``` 25 | 26 | See [this Ellie](https://ellie-app.com/gCQvcN4gy3Ra1). 27 | 28 | 29 | 30 | # How to bootstrap a new project 31 | 32 | You can find a fully functional project in the folder [`examples/pwa`](https://github.com/rakutentech/r10/tree/master/examples/pwa). You can use it as a base for a new project simply copying it. 33 | 34 | From the root folder of the example, run 35 | 36 | ```bash 37 | npm install 38 | npm start 39 | ``` 40 | 41 | Then you can preview the website at http://localhost:8000. 42 | 43 | Changing `src/Main.elm` will automatically recompile the application and refresh the browser. 44 | 45 | To build the files optimized for the release in production, run: 46 | 47 | ```bash 48 | npm run build 49 | ``` 50 | 51 | You can then find the files in `elm-stuff/elm-starter-files/build`. 52 | 53 | 54 | ## Website characteristics 55 | 56 | The bootstrapped website showcases has these characteristics: 57 | 58 | * Single Page Application (**SPA**) 59 | * Progressive Web Application (**PWA**) 60 | * **Static SSR** (pre-render during the build) 61 | * Works **off-line** and **without Javascript** 62 | * **Installable** on desktop and mobile 63 | * Friendly to search engines (**SEO**) 64 | * Supports **custom previews** so it looks good when you share links 65 | * **Best practices**, **Accessibility**, etc. (High scores with Lighthouse) 66 | * Includes **R10 building blocks** such as logos, icons, buttons, forms, etc. with coding playgrounds. 67 | * Supports **multi language** websites 68 | * Standard **header and footer** 69 | 70 | To know more, read the [`elm-starter` documentation](https://github.com/lucamug/elm-starter) and the [`r10` documentation](https://package.elm-lang.org/packages/rakutentech/r10/latest/). 71 | 72 | Note: If you copied the file in a new folder you need to modify the `elm.json` file: 73 | 74 | 1. Remove `../../src` from the list of `source-directories` 75 | 2. Run `elm install rakutentech/r10` that will add this library as dependency 76 | 77 | 78 | 79 | # Other languages or frameworks 80 | 81 | If you are looking for Rakuten UI components written in other languages or frameworks, have a look at the [ReX Github repository](https://github.com/rakuten-rex) and the [ReX Frontend Components Library](https://zeroheight.com/390c074f3/p/080991-). 82 | 83 | 84 | 85 | # Thanks 86 | 87 | Thanks to Evan Czaplicki, Matthew Griffith, Richard Feldman, the folks at NoRedInk, Ryan Haskell-Glatz, Ilias Van Peer, Aaron VonderHaar, Abadi Kurniawaan, Dillon Kearns, Jeroen Engels, Keith Lazuka, Luke Westby, Alex Korban, Thibaut Assus, Brian Hicks and many more from the Elm community that directly or indirectly supported us in this journey. 88 | 89 | 90 | 91 | # Examples 92 | 93 | These are real-life fully working code samples that render these views: 94 | 95 | ![Examples](https://r10.netlify.app/images/examples-600.png) 96 | 97 | The primary color and the light/dark mode can be changed through the `theme` definitions. For example: 98 | 99 | ```elm 100 | theme = 101 | { mode = R10.Mode.Light 102 | , primaryColor = R10.Color.primary.blueSky 103 | } 104 | 105 | theme = 106 | { mode = R10.Mode.Light 107 | , primaryColor = R10.Color.primary.green 108 | } 109 | 110 | theme = 111 | { mode = R10.Mode.Dark 112 | , primaryColor = R10.Color.primary.blueSky 113 | } 114 | ``` 115 | 116 | This is the source code for the example with two buttons: [Source code](https://github.com/rakutentech/r10/tree/master/examples/simpleView/src/Main.elm). 117 | 118 | This is the code sample for the view with the form: ([Source code](https://github.com/rakutentech/r10/blob/master/examples/simpleForm/src/Main.elm)). 119 | -------------------------------------------------------------------------------- /examples/simpleForm/src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | import Browser 4 | import Element.WithContext exposing (..) 5 | import Element.WithContext.Font as Font 6 | import Html 7 | import R10.Button 8 | import R10.Card 9 | import R10.Color.AttrsBackground 10 | import R10.Color.Svg 11 | import R10.Context 12 | import R10.FontSize 13 | import R10.Form 14 | import R10.FormTypes 15 | import R10.Libu 16 | import R10.Paragraph 17 | import R10.Svg.LogosExtra 18 | 19 | 20 | main : Program () Model Msg 21 | main = 22 | Browser.element 23 | { init = init 24 | , view = view 25 | , update = update 26 | , subscriptions = \_ -> Sub.none 27 | } 28 | 29 | 30 | type alias Model = 31 | { form : R10.Form.Form } 32 | 33 | 34 | type Msg 35 | = MsgForm R10.Form.Msg 36 | 37 | 38 | init : () -> ( Model, Cmd msg ) 39 | init _ = 40 | ( { form = 41 | { conf = 42 | [ R10.Form.entity.field 43 | { id = "email" 44 | , idDom = Nothing 45 | , type_ = R10.FormTypes.TypeText R10.FormTypes.TextEmail 46 | , label = "Email" 47 | , clickableLabel = False 48 | , helperText = Just "Helper text for Email" 49 | , requiredLabel = Just "(required)" 50 | , validationSpecs = 51 | Just 52 | { pretendIsNotValidatedIfValid = True 53 | , showAlsoPassedValidation = False 54 | , validationIcon = R10.FormTypes.NoIcon 55 | , validation = 56 | [ R10.Form.commonValidation.email 57 | , R10.Form.validation.minLength 5 58 | , R10.Form.validation.maxLength 50 59 | , R10.Form.validation.required 60 | ] 61 | } 62 | , minWidth = Nothing 63 | , maxWidth = Nothing 64 | , autocomplete = Nothing 65 | , placeholder = Nothing 66 | , allowOverMaxLength = False 67 | } 68 | , R10.Form.entity.field 69 | { id = "password" 70 | , idDom = Nothing 71 | , type_ = R10.FormTypes.TypeText (R10.FormTypes.TextPasswordNew "Show password") 72 | , label = "Password" 73 | , clickableLabel = False 74 | , helperText = Just "Helper text for Password" 75 | , requiredLabel = Just "(required)" 76 | , validationSpecs = 77 | Just 78 | { pretendIsNotValidatedIfValid = True 79 | , showAlsoPassedValidation = False 80 | , validationIcon = R10.FormTypes.NoIcon 81 | , validation = 82 | [ R10.Form.validation.minLength 8 83 | , R10.Form.validation.required 84 | ] 85 | } 86 | , minWidth = Nothing 87 | , maxWidth = Nothing 88 | , autocomplete = Nothing 89 | , placeholder = Nothing 90 | , allowOverMaxLength = False 91 | } 92 | ] 93 | , state = R10.Form.initState 94 | } 95 | } 96 | , Cmd.none 97 | ) 98 | 99 | 100 | update : Msg -> Model -> ( Model, Cmd msg ) 101 | update msg model = 102 | case msg of 103 | MsgForm msgForm -> 104 | let 105 | form : R10.Form.Form 106 | form = 107 | model.form 108 | 109 | ( newState, cmd ) = 110 | R10.Form.update (\_ a -> a) msgForm form.state 111 | 112 | newForm : R10.Form.Form 113 | newForm = 114 | { form | state = newState } 115 | in 116 | ( { model | form = newForm }, Cmd.none ) 117 | 118 | 119 | view : Model -> Html.Html Msg 120 | view model = 121 | layoutWith R10.Context.default 122 | { options = 123 | [ focusStyle 124 | { borderColor = Nothing 125 | , backgroundColor = Nothing 126 | , shadow = Nothing 127 | } 128 | ] 129 | } 130 | [ R10.Color.AttrsBackground.background, padding 20, R10.FontSize.normal ] 131 | (column 132 | (R10.Card.high 133 | ++ [ centerX 134 | , centerY 135 | , width (fill |> maximum 360) 136 | , height shrink 137 | , spacing 30 138 | ] 139 | ) 140 | [ withContext <| \c -> R10.Svg.LogosExtra.r10 [ centerX ] (R10.Color.Svg.logo c.contextR10.theme) 32 141 | , R10.Paragraph.normalMarkdown [ Font.center ] "This is an example of a form made with [Elm](https://elm-lang.org/), [elm-ui](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/) and [R10](https://package.elm-lang.org/packages/rakutentech/r10/latest/) ([Source code](https://github.com/rakutentech/r10/blob/master/examples/simpleForm/src/Main.elm))." 142 | , column [ spacing 20, width fill ] <| R10.Form.view model.form MsgForm 143 | , map MsgForm <| 144 | R10.Button.primary [] 145 | { label = text "Sign In" 146 | , libu = R10.Libu.Bu <| Just <| R10.Form.msg.submit model.form.conf 147 | , translation = { key = "example" } 148 | } 149 | ] 150 | ) 151 | -------------------------------------------------------------------------------- /src/R10/FormComponents/Internal/UI/Color.elm: -------------------------------------------------------------------------------- 1 | module R10.FormComponents.Internal.UI.Color exposing 2 | ( background 3 | , backgroundA 4 | , border 5 | , borderA 6 | , container 7 | , containerA 8 | , error 9 | , errorA 10 | , font 11 | , fontA 12 | , fromPalette 13 | , fromPaletteColor 14 | , label 15 | , labelA 16 | , mouseOverPrimary 17 | , mouseOverSurface 18 | , onPrimary 19 | , onPrimaryA 20 | , onSurface 21 | , onSurfaceA 22 | , primary 23 | , primaryA 24 | , primaryVariant 25 | , primaryVariantA 26 | , success 27 | , successA 28 | , surface 29 | , surfaceA 30 | , toCssString 31 | , transparent 32 | ) 33 | 34 | import Color 35 | import Color.Blending 36 | import Element.WithContext exposing (..) 37 | import R10.Context exposing (..) 38 | import R10.Palette 39 | 40 | 41 | 42 | -- helpers 43 | 44 | 45 | fromPaletteColor : Color.Color -> Color 46 | fromPaletteColor = 47 | Color.toRgba >> fromRgb 48 | 49 | 50 | fromPalette : (R10.Palette.Palette -> Color.Color) -> R10.Palette.Palette -> Color 51 | fromPalette mapper = 52 | mapper >> fromPaletteColor 53 | 54 | 55 | toPaletteColor : Color -> Color.Color 56 | toPaletteColor = 57 | toRgb >> Color.fromRgba 58 | 59 | 60 | toCssString : Color -> String 61 | toCssString = 62 | toPaletteColor >> Color.toCssString 63 | 64 | 65 | 66 | -- Palette colors 67 | 68 | 69 | primary : R10.Palette.Palette -> Color 70 | primary = 71 | .primary >> fromPaletteColor 72 | 73 | 74 | primaryVariant : R10.Palette.Palette -> Color 75 | primaryVariant = 76 | .primaryVariant >> fromPaletteColor 77 | 78 | 79 | success : R10.Palette.Palette -> Color 80 | success = 81 | .success >> fromPaletteColor 82 | 83 | 84 | error : R10.Palette.Palette -> Color 85 | error = 86 | .error >> fromPaletteColor 87 | 88 | 89 | onSurface : R10.Palette.Palette -> Color 90 | onSurface = 91 | .onSurface >> fromPaletteColor 92 | 93 | 94 | onPrimary : R10.Palette.Palette -> Color 95 | onPrimary = 96 | .onPrimary >> fromPaletteColor 97 | 98 | 99 | surface : R10.Palette.Palette -> Color 100 | surface = 101 | .surface >> fromPaletteColor 102 | 103 | 104 | background : R10.Palette.Palette -> Color 105 | background = 106 | .background >> fromPaletteColor 107 | 108 | 109 | 110 | -- Palette colors helpers 111 | 112 | 113 | primaryA : Float -> R10.Palette.Palette -> Color 114 | primaryA alpha palette = 115 | palette.primary 116 | |> R10.Palette.withOpacity alpha 117 | |> fromPaletteColor 118 | 119 | 120 | primaryVariantA : Float -> R10.Palette.Palette -> Color 121 | primaryVariantA alpha palette = 122 | palette.primaryVariant 123 | |> R10.Palette.withOpacity alpha 124 | |> fromPaletteColor 125 | 126 | 127 | successA : Float -> R10.Palette.Palette -> Color 128 | successA alpha palette = 129 | palette.success 130 | |> R10.Palette.withOpacity alpha 131 | |> fromPaletteColor 132 | 133 | 134 | errorA : Float -> R10.Palette.Palette -> Color 135 | errorA alpha palette = 136 | palette.error 137 | |> R10.Palette.withOpacity alpha 138 | |> fromPaletteColor 139 | 140 | 141 | onSurfaceA : Float -> R10.Palette.Palette -> Color 142 | onSurfaceA alpha palette = 143 | palette.onSurface 144 | |> R10.Palette.withOpacity alpha 145 | |> fromPaletteColor 146 | 147 | 148 | onPrimaryA : Float -> R10.Palette.Palette -> Color 149 | onPrimaryA alpha palette = 150 | palette.onPrimary 151 | |> R10.Palette.withOpacity alpha 152 | |> fromPaletteColor 153 | 154 | 155 | surfaceA : Float -> R10.Palette.Palette -> Color 156 | surfaceA alpha palette = 157 | palette.surface 158 | |> R10.Palette.withOpacity alpha 159 | |> fromPaletteColor 160 | 161 | 162 | backgroundA : Float -> R10.Palette.Palette -> Color 163 | backgroundA alpha palette = 164 | palette.background 165 | |> R10.Palette.withOpacity alpha 166 | |> fromPaletteColor 167 | 168 | 169 | 170 | -- color aliases 171 | 172 | 173 | container : R10.Palette.Palette -> Color 174 | container = 175 | onSurfaceA 0.54 176 | 177 | 178 | containerA : Float -> R10.Palette.Palette -> Color 179 | containerA alpha = 180 | onSurfaceA <| 0.54 * alpha 181 | 182 | 183 | font : R10.Palette.Palette -> Color 184 | font = 185 | onSurfaceA 0.87 186 | 187 | 188 | fontA : Float -> R10.Palette.Palette -> Color 189 | fontA alpha = 190 | onSurfaceA <| 0.87 * alpha 191 | 192 | 193 | label : R10.Palette.Palette -> Color 194 | label = 195 | -- TODO(This is a temporary fix for OMNI (The label color is too light)) 196 | onSurfaceA 1 197 | 198 | 199 | labelA : Float -> R10.Palette.Palette -> Color 200 | labelA alpha = 201 | -- TODO(This is a temporary fix for OMNI) 202 | onSurfaceA <| 1 * alpha 203 | 204 | 205 | mouseOverSurface : R10.Palette.Palette -> Color 206 | mouseOverSurface = 207 | -- https://material.io/design/interaction/states.html#hover 208 | onSurfaceA 0.04 209 | 210 | 211 | mouseOverPrimary : R10.Palette.Palette -> Color 212 | mouseOverPrimary palette = 213 | -- https://material.io/design/interaction/states.html#hover 214 | Color.Blending.overlay (onPrimaryA 0.08 palette |> toPaletteColor) (primary palette |> toPaletteColor) 215 | |> fromPaletteColor 216 | 217 | 218 | 219 | -- constant colors 220 | 221 | 222 | transparent : Color 223 | transparent = 224 | R10.Palette.black 225 | |> R10.Palette.withOpacity 0 226 | |> fromPaletteColor 227 | 228 | 229 | 230 | -- border 231 | 232 | 233 | border : R10.Palette.Palette -> Color 234 | border palette = 235 | palette.border 236 | |> fromPaletteColor 237 | 238 | 239 | borderA : Float -> R10.Palette.Palette -> Color 240 | borderA alpha palette = 241 | palette.border 242 | |> R10.Palette.withOpacity alpha 243 | |> fromPaletteColor 244 | --------------------------------------------------------------------------------
` which can be styled via CSS. You can do crazy stuff with 19 | `