mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2025-01-03 12:02:19 +03:00
Merge pull request #326 from NoRedInk/tessa/update-modal
Tessa/update modal
This commit is contained in:
commit
9104f54056
3
elm.json
3
elm.json
@ -45,6 +45,7 @@
|
||||
"Nri.Ui.Modal.V3",
|
||||
"Nri.Ui.Modal.V4",
|
||||
"Nri.Ui.Modal.V5",
|
||||
"Nri.Ui.Modal.V6",
|
||||
"Nri.Ui.Outline.V2",
|
||||
"Nri.Ui.Page.V2",
|
||||
"Nri.Ui.Page.V3",
|
||||
@ -96,4 +97,4 @@
|
||||
"test-dependencies": {
|
||||
"elm-explorations/test": "1.2.0 <= v < 2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
295
src/Nri/Ui/Modal/V6.elm
Normal file
295
src/Nri/Ui/Modal/V6.elm
Normal file
@ -0,0 +1,295 @@
|
||||
module Nri.Ui.Modal.V6 exposing
|
||||
( Model, init
|
||||
, Msg, update, subscriptions
|
||||
, open, close
|
||||
, info, warning, FocusableElementAttrs
|
||||
, viewContent, viewFooter
|
||||
, closeButton
|
||||
)
|
||||
|
||||
{-| Changes from V5:
|
||||
|
||||
- Removes button helpers, now that we can use Nri.Ui.Button.V9 directly
|
||||
|
||||
These changes have required major API changes. Be sure to wire up subscriptions!
|
||||
|
||||
import Html.Styled exposing (..)
|
||||
import Nri.Ui.Button.V9 as Button
|
||||
import Nri.Ui.Modal.V6 as Modal
|
||||
|
||||
view : Modal.State -> Html Msg
|
||||
view state =
|
||||
Modal.info
|
||||
{ title = { title = "Modal Header", visibleTitle = True }
|
||||
, wrapMsg = ModalMsg
|
||||
, content =
|
||||
\{ onlyFocusableElement } ->
|
||||
div []
|
||||
[ Modal.viewContent [ text "Content goes here!" ]
|
||||
, Modal.viewFooter
|
||||
[ Button.button "Continue"
|
||||
[ Button.primary
|
||||
, Button.onClick DoSomthing
|
||||
, Button.custom onlyFocusableElement
|
||||
]
|
||||
, text "`onlyFocusableElement` will trap the focus on the 'Continue' button."
|
||||
]
|
||||
]
|
||||
}
|
||||
state
|
||||
|
||||
subscriptions : Modal.State -> Sub Msg
|
||||
subscriptions state =
|
||||
Modal.subscriptions state
|
||||
|
||||
|
||||
## State and updates
|
||||
|
||||
@docs Model, init
|
||||
@docs Msg, update, subscriptions
|
||||
|
||||
@docs open, close
|
||||
|
||||
|
||||
## Views
|
||||
|
||||
|
||||
### Modals
|
||||
|
||||
@docs info, warning, FocusableElementAttrs
|
||||
|
||||
|
||||
### View containers
|
||||
|
||||
@docs viewContent, viewFooter
|
||||
|
||||
|
||||
## X icon
|
||||
|
||||
@docs closeButton
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Modal as Modal
|
||||
import Accessibility.Style
|
||||
import Accessibility.Styled as Html exposing (..)
|
||||
import Accessibility.Styled.Style
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Color
|
||||
import Css
|
||||
import Css.Global
|
||||
import Html as Root
|
||||
import Html.Attributes exposing (style)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Html.Styled.Events exposing (onClick)
|
||||
import Nri.Ui
|
||||
import Nri.Ui.Colors.Extra
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.SpriteSheet
|
||||
import Nri.Ui.Svg.V1
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Model =
|
||||
Modal.Model
|
||||
|
||||
|
||||
{-| -}
|
||||
init : Model
|
||||
init =
|
||||
Modal.init
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Msg =
|
||||
Modal.Msg
|
||||
|
||||
|
||||
{-| Include the subscription if you want the modal to dismiss on `Esc`.
|
||||
-}
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions =
|
||||
Modal.subscriptions
|
||||
|
||||
|
||||
{-| -}
|
||||
update : { dismissOnEscAndOverlayClick : Bool } -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
update config msg model =
|
||||
Modal.update config msg model
|
||||
|
||||
|
||||
{-| -}
|
||||
close : Msg
|
||||
close =
|
||||
Modal.close
|
||||
|
||||
|
||||
{-| Pass the id of the element that focus should return to when the modal closes.
|
||||
-}
|
||||
open : String -> Msg
|
||||
open =
|
||||
Modal.open
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias FocusableElementAttrs msg =
|
||||
{ onlyFocusableElement : List (Attribute msg)
|
||||
, firstFocusableElement : List (Attribute msg)
|
||||
, lastFocusableElement : List (Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
info :
|
||||
{ visibleTitle : Bool
|
||||
, title : String
|
||||
, content : FocusableElementAttrs msg -> Html msg
|
||||
, wrapMsg : Msg -> msg
|
||||
}
|
||||
-> Model
|
||||
-> Html msg
|
||||
info config model =
|
||||
view { overlayColor = Colors.navy, titleColor = Colors.navy } config model
|
||||
|
||||
|
||||
{-| -}
|
||||
warning :
|
||||
{ visibleTitle : Bool
|
||||
, title : String
|
||||
, content : FocusableElementAttrs msg -> Html msg
|
||||
, wrapMsg : Msg -> msg
|
||||
}
|
||||
-> Model
|
||||
-> Html msg
|
||||
warning config model =
|
||||
view { overlayColor = Colors.gray20, titleColor = Colors.red } config model
|
||||
|
||||
|
||||
view :
|
||||
{ overlayColor : Css.Color, titleColor : Css.Color }
|
||||
->
|
||||
{ visibleTitle : Bool
|
||||
, title : String
|
||||
, content : FocusableElementAttrs msg -> Html msg
|
||||
, wrapMsg : Msg -> msg
|
||||
}
|
||||
-> Model
|
||||
-> Html msg
|
||||
view { overlayColor, titleColor } config model =
|
||||
Modal.view
|
||||
{ overlayColor = toOverlayColor overlayColor
|
||||
, wrapMsg = config.wrapMsg
|
||||
, modalAttributes = modalStyles
|
||||
, title = viewTitle titleColor { title = config.title, visibleTitle = config.visibleTitle }
|
||||
, content =
|
||||
\{ onlyFocusableElement, firstFocusableElement, lastFocusableElement } ->
|
||||
{ onlyFocusableElement = List.map Html.Styled.Attributes.fromUnstyled onlyFocusableElement
|
||||
, firstFocusableElement = List.map Html.Styled.Attributes.fromUnstyled firstFocusableElement
|
||||
, lastFocusableElement = List.map Html.Styled.Attributes.fromUnstyled lastFocusableElement
|
||||
}
|
||||
|> config.content
|
||||
|> toUnstyled
|
||||
}
|
||||
model
|
||||
|> fromUnstyled
|
||||
|
||||
|
||||
toOverlayColor : Css.Color -> String
|
||||
toOverlayColor color =
|
||||
toCssString (Nri.Ui.Colors.Extra.withAlpha 0.9 color)
|
||||
|
||||
|
||||
modalStyles : List (Root.Attribute Never)
|
||||
modalStyles =
|
||||
[ style "width" "600px"
|
||||
, style "max-height" "calc(100vh - 100px)"
|
||||
, style "padding" "40px 0 40px 0"
|
||||
, style "margin" "75px auto"
|
||||
, style "background-color" (toCssString Colors.white)
|
||||
, style "border-radius" "20px"
|
||||
, style "box-shadow" "0 1px 10px 0 rgba(0, 0, 0, 0.35)"
|
||||
, style "position" "relative" -- required for closeButtonContainer
|
||||
]
|
||||
|
||||
|
||||
{-| -}
|
||||
viewTitle : Css.Color -> { visibleTitle : Bool, title : String } -> ( String, List (Root.Attribute Never) )
|
||||
viewTitle color { visibleTitle, title } =
|
||||
( title
|
||||
, if visibleTitle then
|
||||
[ style "font-weight" "700"
|
||||
, style "line-height" "27px"
|
||||
, style "margin" "0 49px"
|
||||
, style "font-size" "20px"
|
||||
, style "text-align" "center"
|
||||
, style "color" (toCssString color)
|
||||
]
|
||||
|
||||
else
|
||||
Accessibility.Style.invisible
|
||||
)
|
||||
|
||||
|
||||
toCssString : Css.Color -> String
|
||||
toCssString =
|
||||
Color.toCssString << Nri.Ui.Colors.Extra.toCoreColor
|
||||
|
||||
|
||||
{-| -}
|
||||
viewContent : List (Html msg) -> Html msg
|
||||
viewContent =
|
||||
Nri.Ui.styled div
|
||||
"modal-content"
|
||||
[ Css.overflowY Css.auto
|
||||
, Css.padding2 (Css.px 30) (Css.px 40)
|
||||
, Css.width (Css.pct 100)
|
||||
, Css.minHeight (Css.px 150)
|
||||
, Css.boxSizing Css.borderBox
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
{-| -}
|
||||
viewFooter : List (Html msg) -> Html msg
|
||||
viewFooter =
|
||||
Nri.Ui.styled div
|
||||
"modal-footer"
|
||||
[ Css.alignItems Css.center
|
||||
, Css.displayFlex
|
||||
, Css.flexDirection Css.column
|
||||
, Css.flexGrow (Css.int 2)
|
||||
, Css.flexWrap Css.noWrap
|
||||
, Css.margin4 (Css.px 20) Css.zero Css.zero Css.zero
|
||||
, Css.width (Css.pct 100)
|
||||
]
|
||||
[]
|
||||
|
||||
|
||||
|
||||
--BUTTONS
|
||||
|
||||
|
||||
{-| -}
|
||||
closeButton : (Msg -> msg) -> List (Attribute msg) -> Html msg
|
||||
closeButton wrapMsg focusableElementAttrs =
|
||||
Nri.Ui.styled button
|
||||
"close-button-container"
|
||||
[ Css.position Css.absolute
|
||||
, Css.top Css.zero
|
||||
, Css.right Css.zero
|
||||
, Css.padding (Css.px 25)
|
||||
, Css.borderWidth Css.zero
|
||||
, Css.width (Css.px 75)
|
||||
, Css.backgroundColor Css.transparent
|
||||
, Css.cursor Css.pointer
|
||||
, Css.color Colors.azure
|
||||
, Css.hover [ Css.color Colors.azureDark ]
|
||||
, Css.property "transition" "color 0.1s"
|
||||
]
|
||||
(Widget.label "Close modal"
|
||||
:: Html.Styled.Attributes.map wrapMsg (onClick Modal.close)
|
||||
:: focusableElementAttrs
|
||||
)
|
||||
[ Nri.Ui.Svg.V1.toHtml Nri.Ui.SpriteSheet.xSvg
|
||||
]
|
@ -12,9 +12,10 @@ import Css.Global
|
||||
import Html as Root
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import ModuleExample exposing (Category(..), ModuleExample)
|
||||
import Nri.Ui.Button.V9 as Button
|
||||
import Nri.Ui.Checkbox.V5 as Checkbox
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Modal.V5 as Modal
|
||||
import Nri.Ui.Modal.V6 as Modal
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -45,29 +46,44 @@ init =
|
||||
{-| -}
|
||||
example : (Msg -> msg) -> State -> ModuleExample msg
|
||||
example parentMessage state =
|
||||
{ name = "Nri.Ui.Modal.V5"
|
||||
{ name = "Nri.Ui.Modal.V6"
|
||||
, category = Modals
|
||||
, content =
|
||||
[ Modal.launchButton InfoModalMsg [] "Launch Info Modal"
|
||||
, Modal.launchButton WarningModalMsg [] "Launch Warning Modal"
|
||||
[ Button.button "Launch Info Modal"
|
||||
[ Button.onClick (InfoModalMsg (Modal.open "launch-info-modal"))
|
||||
, Button.custom
|
||||
[ Html.Styled.Attributes.id "launch-info-modal"
|
||||
, css [ Css.marginRight (Css.px 16) ]
|
||||
]
|
||||
, Button.secondary
|
||||
, Button.medium
|
||||
]
|
||||
, Button.button "Launch Warning Modal"
|
||||
[ Button.onClick (WarningModalMsg (Modal.open "launch-warning-modal"))
|
||||
, Button.custom [ Html.Styled.Attributes.id "launch-warning-modal" ]
|
||||
, Button.secondary
|
||||
, Button.medium
|
||||
]
|
||||
, Modal.info
|
||||
{ title = { title = "Modal.info", visibleTitle = state.visibleTitle }
|
||||
{ title = "Modal.info"
|
||||
, visibleTitle = state.visibleTitle
|
||||
, wrapMsg = InfoModalMsg
|
||||
, content =
|
||||
viewContent state
|
||||
InfoModalMsg
|
||||
(Modal.primaryButton ForceClose "Continue")
|
||||
(Modal.secondaryButton ForceClose "Close")
|
||||
Button.primary
|
||||
Button.secondary
|
||||
}
|
||||
state.infoModal
|
||||
, Modal.warning
|
||||
{ title = { title = "Modal.warning", visibleTitle = state.visibleTitle }
|
||||
{ title = "Modal.warning"
|
||||
, visibleTitle = state.visibleTitle
|
||||
, wrapMsg = WarningModalMsg
|
||||
, content =
|
||||
viewContent state
|
||||
WarningModalMsg
|
||||
(Modal.dangerButton ForceClose "Continue")
|
||||
(Modal.secondaryButton ForceClose "Close")
|
||||
Button.danger
|
||||
Button.secondary
|
||||
}
|
||||
state.warningModal
|
||||
]
|
||||
@ -78,37 +94,106 @@ example parentMessage state =
|
||||
viewContent :
|
||||
State
|
||||
-> (Modal.Msg -> Msg)
|
||||
-> (List (Root.Attribute Msg) -> Html Msg)
|
||||
-> (List (Root.Attribute Msg) -> Html Msg)
|
||||
-> Button.Attribute Msg
|
||||
-> Button.Attribute Msg
|
||||
-> Modal.FocusableElementAttrs Msg
|
||||
-> Html Msg
|
||||
viewContent state wrapMsg primaryButton secondaryButton focusableElementAttrs =
|
||||
div []
|
||||
[ if state.showX then
|
||||
Modal.closeButton wrapMsg focusableElementAttrs.firstFocusableElement
|
||||
|
||||
else
|
||||
text ""
|
||||
, Modal.viewContent [ viewSettings state ]
|
||||
, if state.showContinue && state.showSecondary then
|
||||
Modal.viewFooter
|
||||
[ primaryButton []
|
||||
, secondaryButton focusableElementAttrs.lastFocusableElement
|
||||
viewContent state wrapMsg firstButtonStyle secondButtonStyle focusableElementAttrs =
|
||||
case ( state.showX, state.showContinue, state.showSecondary ) of
|
||||
( True, True, True ) ->
|
||||
div []
|
||||
[ Modal.closeButton wrapMsg focusableElementAttrs.firstFocusableElement
|
||||
, Modal.viewContent [ viewSettings state ]
|
||||
, Modal.viewFooter
|
||||
[ Button.button "Continue"
|
||||
[ firstButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
]
|
||||
, Button.button "Close"
|
||||
[ secondButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
, Button.custom focusableElementAttrs.lastFocusableElement
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
else if state.showContinue then
|
||||
Modal.viewFooter
|
||||
[ primaryButton focusableElementAttrs.lastFocusableElement
|
||||
( True, False, True ) ->
|
||||
div []
|
||||
[ Modal.closeButton wrapMsg focusableElementAttrs.firstFocusableElement
|
||||
, Modal.viewContent [ viewSettings state ]
|
||||
, Modal.viewFooter
|
||||
[ Button.button "Close"
|
||||
[ secondButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
, Button.custom focusableElementAttrs.lastFocusableElement
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
else if state.showSecondary then
|
||||
Modal.viewFooter
|
||||
[ secondaryButton focusableElementAttrs.lastFocusableElement
|
||||
( True, False, False ) ->
|
||||
div []
|
||||
[ Modal.closeButton wrapMsg focusableElementAttrs.firstFocusableElement
|
||||
, Modal.viewContent [ viewSettings state ]
|
||||
]
|
||||
|
||||
else
|
||||
text ""
|
||||
]
|
||||
( True, True, False ) ->
|
||||
div []
|
||||
[ Modal.closeButton wrapMsg focusableElementAttrs.firstFocusableElement
|
||||
, Modal.viewContent [ viewSettings state ]
|
||||
, Modal.viewFooter
|
||||
[ Button.button "Continue"
|
||||
[ firstButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
, Button.custom focusableElementAttrs.lastFocusableElement
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
( False, True, True ) ->
|
||||
div []
|
||||
[ Modal.viewContent [ viewSettings state ]
|
||||
, Modal.viewFooter
|
||||
[ Button.button "Continue"
|
||||
[ firstButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
, Button.custom focusableElementAttrs.firstFocusableElement
|
||||
]
|
||||
, Button.button "Close"
|
||||
[ secondButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
, Button.custom focusableElementAttrs.lastFocusableElement
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
( False, False, True ) ->
|
||||
div []
|
||||
[ Modal.viewContent [ viewSettings state ]
|
||||
, Modal.viewFooter
|
||||
[ Button.button "Close"
|
||||
[ secondButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
, Button.custom focusableElementAttrs.lastFocusableElement
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
( False, True, False ) ->
|
||||
div []
|
||||
[ Modal.viewContent [ viewSettings state ]
|
||||
, Modal.viewFooter
|
||||
[ Button.button "Continue"
|
||||
[ firstButtonStyle
|
||||
, Button.onClick ForceClose
|
||||
, Button.custom focusableElementAttrs.lastFocusableElement
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
( False, False, False ) ->
|
||||
div []
|
||||
[ Modal.viewContent [ viewSettings state ]
|
||||
]
|
||||
|
||||
|
||||
viewSettings : State -> Html Msg
|
||||
|
Loading…
Reference in New Issue
Block a user