Implement new Outline styling

This doesn't include the entire spec for the new outline styling, just
the part we currently need for content creation outline draggable
creation. The additions to complete the spec look doable in the current
structure though.
This commit is contained in:
Jasper Woudenberg 2018-01-16 09:18:23 +01:00
parent e2c1c8893a
commit 5d44e2da3f
6 changed files with 430 additions and 944 deletions

View File

@ -1,37 +1,36 @@
{
"version": "2.9.0",
"summary": "Doodads we use at NRI",
"repository": "https://github.com/NoRedInk/elm-doodad.git",
"license": "BSD3",
"source-directories": [
"src"
],
"exposed-modules": [
"Nri.Alert",
"Nri.BannerAlert",
"Nri.Button",
"Nri.Divider",
"Nri.Modal",
"Nri.Outline",
"Nri.Outline.Types",
"Nri.Palette",
"Nri.Styles",
"Nri.Tabs",
"Nri.Text",
"Nri.TextAreaWithOverlappingLabel",
"Nri.TextInput"
],
"dependencies": {
"NoRedInk/nri-elm-css": "5.0.0 <= v <= 5.0.0",
"NoRedInk/view-extra": "2.0.0 <= v < 3.0.0",
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"pablohirafuji/elm-markdown": "2.0.4 <= v < 3.0.0",
"rtfeldman/elm-css": "11.2.0 <= v < 12.0.0",
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",
"rtfeldman/elm-css-util": "1.0.2 <= v < 2.0.0",
"tesk9/accessible-html": "3.0.0 <= v < 4.0.0",
"wernerdegroot/listzipper": "3.0.0 <= v < 4.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
"version": "2.9.0",
"summary": "Doodads we use at NRI",
"repository": "https://github.com/NoRedInk/elm-doodad.git",
"license": "BSD3",
"source-directories": [
"src"
],
"exposed-modules": [
"Nri.Alert",
"Nri.BannerAlert",
"Nri.Button",
"Nri.Divider",
"Nri.Modal",
"Nri.Outline",
"Nri.Palette",
"Nri.Styles",
"Nri.Tabs",
"Nri.Text",
"Nri.TextAreaWithOverlappingLabel",
"Nri.TextInput"
],
"dependencies": {
"NoRedInk/nri-elm-css": "5.0.0 <= v <= 5.0.0",
"NoRedInk/view-extra": "2.0.0 <= v < 3.0.0",
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"pablohirafuji/elm-markdown": "2.0.4 <= v < 3.0.0",
"rtfeldman/elm-css": "11.2.0 <= v < 12.0.0",
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0",
"rtfeldman/elm-css-util": "1.0.2 <= v < 2.0.0",
"tesk9/accessible-html": "3.0.0 <= v < 4.0.0",
"wernerdegroot/listzipper": "3.0.0 <= v < 4.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

35
src/Nri/Effects.elm Normal file
View File

@ -0,0 +1,35 @@
module Nri.Effects exposing (selectionShadow)
{-| Css mixins reused across Nri modules.
@docs selectionShadow
-}
import Css exposing (..)
import Nri.Colors
{-| Draw a 2 px thick ochre border around the element to indicate it is
selected.
This uses a CSS box shadow to draw what looks like a border. Box shadows are
perfect for this because they don't affect the elements positioning in any way.
This means we can be sure switching the selection shadow on and off is not
going to make the element jump.
-}
selectionShadow : List Style
selectionShadow =
-- There should appear to be 2px of space between the element outline and
-- the surrounding selection border. To accomplish this we use two box
-- shadows, a inner white shadow and an outer ochre one.
-- Elm-css does not support multiple box shadows, so we build up that the
-- CSS value manually.
[ Css.property "box-shadow" ("0 0 0 2px white, 0 0 0 4px " ++ colorToString Nri.Colors.ochre)
]
colorToString : Css.Color -> String
colorToString { red, green, blue } =
String.concat [ "rgb(", toString red, ",", toString green, ",", toString blue, ")" ]

View File

@ -1,181 +1,385 @@
module Nri.Outline
exposing
( OutlineLayout
, container
, customRow
, row
, rowWithExtraContent
, styles
, withEvaluated
, withTitle
)
module Nri.Outline exposing (NodeConfig, NodeLayout, config, html, node, segment, styles)
{-| A nestedable layout that can be themed.
## Types
@docs OutlineLayout
## Main container
@docs container
## Rows
@docs row, rowWithExtraContent, customRow
## Row modifiers
@docs withTitle, withEvaluated
## Styles
{-| A module for rendering outline layouts.
@docs segment
@docs node
@docs NodeLayout
@docs NodeConfig
@docs config
@docs html
@docs styles
-}
import Html exposing (..)
import Nri.Outline.Styles as Styles exposing (styles)
import Nri.Outline.Types as Types
import Nri.Outline.Utils as Utils
import Nri.Styles
import Css exposing (Stylesheet)
import Html exposing (Attribute, Html)
import Nri.Colors
import Nri.Effects
import Nri.Palette exposing (Palette)
import Nri.Stylers
import Nri.Styles exposing (Styles)
{-| Aliased strictly for exporting
{-| A wrapper for a node rendered into Html. This type exists to prevent us
from accidentally wrapping a node in a container element before passing it as a
child to another node or segment. Such wrapping would break some of our
styling, which assumes nodes of the same level are sibblings in the Html tree.
-}
type alias OutlineLayout msg =
Types.OutlineLayout msg
type NodeLayout msg
= NodeLayout (Html msg)
{-| The row container. This is required as it injects the styles for the rows into a style tag.
{-| A container to draw nodes in.
import Html exposing (..)
import Nri.Outline as Outline
main : Html msg
main =
div []
[ Outline.container
[{- Extra attributes go here -}]
[{- Rows go here -}]
]
segment
[ node { config | label = "First Node" }
, node { config | label = "Second Node" }
]
-}
container : List (Attribute msg) -> List (Types.OutlineLayout msg) -> Html msg
container attributes rows =
ul (attributes ++ [ styles.class [ Styles.Container ] ])
(List.map (Utils.rowToHtml Types.Root) rows)
segment : List (NodeLayout msg) -> Html msg
segment children =
styles.div Segment (List.map unlayout children)
{-| Render an unstyled row with only the outline styles.
{-| Wrap any html in a NodeLayout so you can use it as sibling content to nodes.
import Html exposing (..)
import Nri.Outline as Outline
segment
[ node { config | label = "This is a node" }
, html (Html.text "This is some random Html content!")
]
main : Html msg
main =
div []
[ Outline.container []
[ Outline.row
{ content = text "This is my content"
, palette = Palette.red
, rows = []
}
-}
html : Html msg -> NodeLayout msg
html child =
styles.div CustomHtml [ child ]
|> NodeLayout
{-| Defines how a node should look.
-}
type alias NodeConfig msg =
-- The node's label.
{ label : Html msg
-- The content of the node (the part in the colored area).
, contents : Html msg
-- Child nodes (and other content) to be placed below the content.
, children : List (NodeLayout msg)
-- The node is selected. Draw a selection shadow around it.
, selected : Bool
-- The node is ghosted. Fade it out.
, ghosted : Bool
-- Addition attributes to be set on the top level node element.
, attrs : List (Html.Attribute msg)
}
{-| A default node configuration, allowing you to only set the properties you care about.
node { config | label = "Claim" }
-}
config : NodeConfig msg
config =
{ label = Html.text ""
, contents = Html.text ""
, children = []
, selected = False
, ghosted = False
, attrs = []
}
{-| Draw a node of an outline structure. You can draw other nodes inside it and
connecting lines will appear.
node { config | label = "Claim" }
-}
node : NodeConfig msg -> NodeLayout msg
node { label, contents, children, selected, attrs, ghosted } =
NodeLayout <|
-- We use a custom Html tag name here, to ensure we can find the first and
-- last node in a last-of-type selector.
Html.node "outline-node"
(styles.classList
[ ( Node, True )
, ( GhostedNode, ghosted )
]
]
-}
row : Types.RowConfig msg -> Types.OutlineLayout msg
row config =
Types.Row <| Utils.rawRow config
{-| -}
customRow : Types.CustomConfig msg -> Types.OutlineLayout msg
customRow config =
Types.Row config
{-| Render a row with extra content. This row cannot have child rows.
import Html exposing (..)
import Nri.Outline as Outline
main : Html msg
main =
div []
[ Outline.container []
[ Outline.rowWithExtraContent
{ content = text "This is my content"
, palette = Palette.red
, extraContent = text "My extra content"
}
:: attrs
)
[ Html.div
[ styles.classList
[ ( InnerNode, True )
, ( SelectedNode, selected )
]
]
(styles.div Label [ label ]
:: styles.div Contents [ contents ]
:: List.map unlayout children
)
]
unlayout : NodeLayout msg -> Html msg
unlayout (NodeLayout html) =
html
type Style
= Segment
| Node
| InnerNode
| SelectedNode
| GhostedNode
| Label
| Contents
| CustomHtml
labelHeight : Float
labelHeight =
35
{-| Styles used by outline structures.
-}
rowWithExtraContent : Types.ExtraContenRowConfig msg -> Types.OutlineLayout msg
rowWithExtraContent config =
Types.Row <|
Utils.rawRowWithExtraContent config
{-| Render a row with a title
import Html exposing (..)
import Nri.Outline as Outline
main : Html msg
main =
div []
[ Outline.container []
[ Outline.withTitle "My Title" <|
Outline.row
{ content = text "This is my content"
, palette = Palette.red
, rows = []
}
]
]
-}
withTitle : String -> Types.OutlineLayout msg -> Types.OutlineLayout msg
withTitle =
Utils.withTitle
{-| Render a row with 'good' emphasis
import Html exposing (..)
import Nri.Outline as Outline
import Nri.Outline.Types as Types
main : Html msg
main =
div []
[ Outline.container []
[ Outline.withEvaluated Types.Good <|
Outline.row
{ content = text "This is my content"
, palette = Palette.red
, rows = []
}
]
]
-}
withEvaluated : Types.Evaluation -> Types.OutlineLayout msg -> Types.OutlineLayout msg
withEvaluated =
Utils.withEvaluated
{-| Styles used by this module
-}
styles : Nri.Styles.Styles Never Styles.CssClasses msg
styles : Styles a Style b
styles =
Styles.styles
Nri.Styles.styles "Outline" <|
[ Css.class Segment
[ Css.position Css.relative
, Css.zIndex (Css.int 0)
-- The overflow property cuts of connecting lines extending from
-- top level nodes.
, Css.overflow Css.auto
]
, Css.class Node
-- The node's relative positioning allows the connecting line to
-- point upward relative from the node's bounding box.
[ Css.position Css.relative
, Css.display Css.block
-- This selects all nodes on a level but the first.
, Css.generalSiblings
[ Css.class Node
-- Add some spacing between nodes of the same level.
[ Css.marginTop (Css.px 20)
, Css.before
[ Css.property "content" "''"
, Css.borderLeft2 (Css.px 1) Css.solid
, Css.batch lineStyles
]
]
]
-- Child nodes have a connecting line and are indented.
, Css.descendants nestedNodeStyles
]
, Css.class InnerNode
[ Css.overflow Css.auto
-- The position and zIndex create a new stacking context. Connecting
-- lines in child nodes of this one will be drawn in this context.
, Css.position Css.relative
, Css.zIndex (Css.int 0)
-- Recursively assign color styles to the different nested levels of
-- the outline structure.
, Css.descendants
(colorStyles
[ Nri.Palette.cornflower
, Nri.Palette.aqua
, Nri.Palette.turquoise
, Nri.Palette.green
]
)
]
, Css.class GhostedNode
[ Css.opacity (Css.num 0.5)
, Css.zIndex (Css.int -1)
, Css.position Css.relative
]
, Css.class Label
[ Css.border2 (Css.px 1) Css.solid
, Css.padding2 Css.zero (Css.px 15)
, Css.fontSize (Css.px 15)
, Css.borderRadius (Css.px labelHeight)
, Css.lineHeight (Css.px (labelHeight - 3))
, Css.height (Css.px labelHeight)
, Css.backgroundColor Nri.Colors.white
, Css.position Css.absolute
, Css.boxSizing Css.borderBox
, Css.top Css.zero
, Css.left Css.zero
, Nri.Stylers.makeFont (Css.px 15) Nri.Colors.gray20
, Css.fontWeight Css.bold
]
, Css.class Contents
[ Css.borderRadius (Css.px 8)
, Css.marginTop (Css.px (labelHeight / 2))
, Css.marginLeft (Css.px (labelHeight / 2))
, Css.minHeight (Css.px 70)
-- Ensure there's some margin on all sides, so we have the option of
-- drawing a border around selected contents without it being cut of
-- by the surrounding inner node.
, Css.marginRight (Css.px 5)
, Css.marginBottom (Css.px 5)
]
, Css.class SelectedNode
[ Css.children
[ Css.class Contents
[ Css.batch Nri.Effects.selectionShadow
]
]
]
]
++ curvedConnectingLineStyles
nestedNodeStyles : List Css.Snippet
nestedNodeStyles =
[ Css.class Node
[ Css.marginTop (Css.px 20)
, Css.before
-- Draw the connect line. It is like an antenna pointing
-- upward in the direction of the parent node.
[ Css.property "content" "''"
, Css.borderLeft2 (Css.px 1) Css.solid
, Css.borderBottom2 (Css.px 1) Css.solid
, Css.batch lineStyles
]
, Css.children
-- Indent this node relative to the parent.
[ Css.class InnerNode
[ Css.marginLeft (Css.px 50)
]
]
]
, Css.class CustomHtml
[ Css.marginLeft (Css.px 50)
]
]
{-| The last node is sometimes connected by a curving line to its parent.
Whether this curve should appear or not depends on the level of the node.
-}
curvedConnectingLineStyles : List Css.Snippet
curvedConnectingLineStyles =
-- 1. Root level nodes are connected with one another using straight lines,
-- so they are never curved.
--
-- Root Node 1
-- │
-- Root Node 2
--
-- 2. Second level nodes have a curved line when they are the last node of
-- their level, and if their parent is the last root node. If either
-- case is not true, they are connected to a straight line that
-- continues beneath then. This is illustrated in the diagram below. As
-- you can see only 'Child Node 3' should have a curved connecting line.
--
-- Root Node 1
-- │
-- ├─ Child Node 1
-- │
-- Root Node 2
-- │
-- ├─ Child Node 2
-- │
-- ╰─ Child Node 3
--
[ Css.class Node
[ Css.lastOfType
[ Css.descendants
[ Css.class Node
[ Css.lastOfType
[ Css.before
[ Css.borderRadius (Css.px 8)
]
]
]
]
]
]
-- 3. Third and lower level nodes always have a curved line when they are
-- the last node on their level. This is illustrated in the diagram
-- below. Sub-Child Node 2 has a curved connecting line, even though it's
-- parent node is not the last node on its level.
--
-- Root Node
-- │
-- ├─ Child Node 1
-- │ │
-- │ ├─ Sub-Child Node 1
-- │ │
-- │ ╰─ Sub-Child Node 2
-- │
-- ╰─ Child Node 2
--
, Css.class Node
[ Css.descendants
[ Css.class Node
[ Css.descendants
[ Css.class Node
[ Css.lastOfType
[ Css.before
[ Css.borderRadius (Css.px 8)
]
]
]
]
]
]
]
]
colorStyles : List Palette -> List Css.Snippet
colorStyles palettes =
case palettes of
[] ->
[]
palette :: rest ->
[ Css.class InnerNode
[ Css.descendants (colorStyles rest)
]
, Css.class Contents
[ Css.backgroundColor palette.background
]
, Css.class Label
[ Css.color palette.primary
, Css.borderColor palette.border
]
]
lineStyles : List Css.Style
lineStyles =
[ Css.display Css.block
, Css.borderColor Nri.Colors.gray75
, Css.position Css.absolute
, Css.width (Css.px 30)
, Css.left (Css.px 30)
, Css.bottom (Css.calc (Css.pct 100) Css.minus (Css.px (labelHeight / 2)))
-- Ensure the connecting line is long enough. The containing element will
-- cut it to size.
, Css.height (Css.vh 10000)
-- Make the connecting line go beneath the parent node,
-- giving the impression it stops when touching the
-- parent.
, Css.zIndex (Css.int -100)
]

View File

@ -1,270 +0,0 @@
module Nri.Outline.Styles exposing (..)
import Css exposing (..)
import Nri.Colors as Colors
import Nri.Css.Extra
import Nri.Fonts
import Nri.Palette as Palette exposing (Palette)
import Nri.Styles
type CssClasses
= Container
| Row
| ChildRow
| RowGray
| RowDarkGray
| RowBlue
| RowDarkBlue
| RowPurple
| RowTurquoise
| RowRed
| RowGreen
| RowWhite
| RowCornflower
| RowAqua
| RowPanelTitle
| RowPanelTitleGray
| RowPanelTitleDarkGray
| RowPanelTitleBlue
| RowPanelTitleDarkBlue
| RowPanelTitlePurple
| RowPanelTitleTurquoise
| RowPanelTitleRed
| RowPanelTitleGreen
| RowPanelTitleWhite
| RowPanelTitleCornflower
| RowPanelTitleAqua
| RowPanelContent
| RowPanelContentGray
| RowPanelContentCornflower
| RowPanelContentDarkGray
| RowPanelContentBlue
| RowPanelContentDarkBlue
| RowPanelContentPurple
| RowPanelContentTurquoise
| RowPanelContentRed
| RowPanelContentGreen
| RowPanelContentWhite
| RowPanelContentAqua
| RowPanelContentDashed
| ChildRowContent
| ChildRowContentCornflower
| ChildRowContentGray
| ChildRowContentDarkGray
| ChildRowContentBlue
| ChildRowContentDarkBlue
| ChildRowContentPurple
| ChildRowContentTurquoise
| ChildRowContentRed
| ChildRowContentGreen
| ChildRowContentWhite
| ChildRowContentAqua
| RowContent
| RowChildren
styles : Nri.Styles.Styles Never CssClasses msg
styles =
Nri.Styles.styles "Nri-Outline-"
[ Css.class Container
[ listStyle none
, margin zero
, padding zero
]
-- rows
, Css.class Row
[ paddingBottom (px 16)
, position relative
, lastChild [ paddingBottom zero ]
]
, Css.class ChildRow
[ after
[ property "content" "''"
, position absolute
, top (px -16)
, left (px -14)
, width (px 14)
, borderLeft3 (px 1) solid (Palette.gray |> .border)
, property "height" "calc(100% + 16px)"
]
, lastChild
[ after [ borderLeftWidth zero ] ]
, onlyChild
{- Fixes border styles if there is only one child row -}
[ after
[ property "content" "''"
, position absolute
, top (px -16)
, left (px -14)
, width (px 14)
, borderLeftWidth (px 1)
, borderLeftStyle solid
, property "height" "16px"
]
]
]
, Css.class RowGray
[ rowTheme Palette.gray ]
, Css.class RowDarkGray
[ rowTheme Palette.darkGray ]
, Css.class RowBlue
[ rowTheme Palette.blue ]
, Css.class RowDarkBlue
[ rowTheme Palette.darkBlue ]
, Css.class RowPurple
[ rowTheme Palette.purple ]
, Css.class RowTurquoise
[ rowTheme Palette.turquoise ]
, Css.class RowRed
[ rowTheme Palette.red ]
, Css.class RowGreen
[ rowTheme Palette.green ]
, Css.class RowWhite
[ rowTheme Palette.white ]
, Css.class RowCornflower
[ rowTheme Palette.cornflower ]
, Css.class RowAqua
[ rowTheme Palette.aqua ]
-- row panel titles
, Css.class RowPanelTitle
[ Nri.Fonts.baseFont
, borderRadius (px 16)
, color Colors.white
, display inlineBlock
, fontSize (px 12)
, height (px 16)
, left (px -4)
, lineHeight (px 16)
, padding2 zero (px 7)
, position absolute
, top (px -8)
, property "z-index" "2"
]
, Css.class RowPanelTitleGray
[ rowPanelTitleTheme Palette.gray ]
, Css.class RowPanelTitleDarkGray
[ rowPanelTitleTheme Palette.darkGray ]
, Css.class RowPanelTitleBlue
[ rowPanelTitleTheme Palette.blue ]
, Css.class RowPanelTitleDarkBlue
[ rowPanelTitleTheme Palette.darkBlue ]
, Css.class RowPanelTitlePurple
[ rowPanelTitleTheme Palette.purple ]
, Css.class RowPanelTitleTurquoise
[ rowPanelTitleTheme Palette.turquoise ]
, Css.class RowPanelTitleRed
[ rowPanelTitleTheme Palette.red ]
, Css.class RowPanelTitleGreen
[ rowPanelTitleTheme Palette.green ]
, Css.class RowPanelTitleWhite
[ rowPanelTitleTheme Palette.white ]
, Css.class RowPanelTitleCornflower
[ rowPanelTitleTheme Palette.cornflower ]
, Css.class RowPanelTitleAqua
[ rowPanelTitleTheme Palette.aqua ]
, Css.class RowPanelContent
[ borderRadius (px 8)
, borderWidth (px 1)
, borderColor (Palette.gray |> .border)
, borderStyle solid
, backgroundColor (Palette.gray |> .background)
, color Colors.gray20
, fontSize (px 18)
, Nri.Fonts.quizFont
, padding (px 13)
, Nri.Css.Extra.lineHeightNum 1.2
]
, Css.class RowPanelContentGray
[ rowPanelContentTheme Palette.gray ]
, Css.class RowPanelContentCornflower
[ rowPanelContentTheme Palette.cornflower ]
, Css.class RowPanelContentDarkGray
[ rowPanelContentTheme Palette.darkGray ]
, Css.class RowPanelContentBlue
[ rowPanelContentTheme Palette.blue ]
, Css.class RowPanelContentDarkBlue
[ rowPanelContentTheme Palette.darkBlue ]
, Css.class RowPanelContentPurple
[ rowPanelContentTheme Palette.purple ]
, Css.class RowPanelContentTurquoise
[ rowPanelContentTheme Palette.turquoise ]
, Css.class RowPanelContentRed
[ rowPanelContentTheme Palette.red ]
, Css.class RowPanelContentGreen
[ rowPanelContentTheme Palette.green ]
, Css.class RowPanelContentWhite
[ rowPanelContentTheme Palette.white ]
, Css.class RowPanelContentAqua
[ rowPanelContentTheme Palette.aqua ]
, Css.class RowPanelContentDashed
[ borderStyle dashed ]
, Css.class ChildRowContent
[ after
[ property "content" "''"
, height (pct 50)
, width (px 14)
, borderBottom3 (px 1) solid (Palette.gray |> .border)
, borderLeft3 (px 1) solid (Palette.gray |> .border)
, left (px -14)
, top zero
, position absolute
, maxHeight (px 50)
]
]
, Css.class ChildRowContentCornflower
[ childRowContentTheme Palette.cornflower ]
, Css.class ChildRowContentGray
[ childRowContentTheme Palette.gray ]
, Css.class ChildRowContentDarkGray
[ childRowContentTheme Palette.darkGray ]
, Css.class ChildRowContentBlue
[ childRowContentTheme Palette.blue ]
, Css.class ChildRowContentDarkBlue
[ childRowContentTheme Palette.darkBlue ]
, Css.class ChildRowContentPurple
[ childRowContentTheme Palette.purple ]
, Css.class ChildRowContentTurquoise
[ childRowContentTheme Palette.turquoise ]
, Css.class ChildRowContentRed
[ childRowContentTheme Palette.red ]
, Css.class ChildRowContentGreen
[ childRowContentTheme Palette.green ]
, Css.class ChildRowContentWhite
[ childRowContentTheme Palette.white ]
, Css.class ChildRowContentAqua
[ childRowContentTheme Palette.aqua ]
, Css.class RowContent
[ position relative ]
, Css.class RowChildren
[ paddingLeft (px 29)
, paddingTop (px 16)
, listStyleType none
]
]
rowTheme : Palette -> Style
rowTheme palette =
batch [ after [ borderColor palette.border ] ]
rowPanelTitleTheme : Palette -> Style
rowPanelTitleTheme palette =
Css.batch [ backgroundColor palette.border ]
childRowContentTheme : Palette -> Style
childRowContentTheme palette =
Css.batch [ after [ borderColor palette.border ] ]
rowPanelContentTheme : Palette -> Style
rowPanelContentTheme palette =
Css.batch
[ backgroundColor palette.background
, borderColor palette.border
, after [ borderColor palette.border ]
]

View File

@ -1,90 +0,0 @@
module Nri.Outline.Types exposing (..)
{-|
@docs BaseConfig, CustomConfig, Evaluation, ExtraContenRowConfig, OutlineLayout, OutlinePanelConfig, RowConfig, RowModifier, RowVerticalAlignment, RowsContent, Hierarchy
-}
import Html exposing (Html)
import Nri.Palette as Palette
{-| -}
type alias BaseConfig msg extras =
{ extras
| content : Html msg
, palette : Palette.Palette
}
{-| -}
type alias CustomConfig msg =
BaseConfig msg
(RowsContent msg
{ verticalAlign : RowVerticalAlignment
, extraContent : Maybe (Html msg)
, modifyRow : RowModifier
, hasDashedBorder : Bool
}
)
{-| -}
type alias RowConfig msg =
BaseConfig msg (RowsContent msg {})
{-| -}
type alias RowsContent msg extras =
{ extras | rows : List (OutlineLayout msg) }
{-| -}
type alias ExtraContenRowConfig msg =
BaseConfig msg
{ rows : List (OutlineLayout msg)
, extraContent : Html msg
}
{-| -}
type alias OutlinePanelConfig msg =
{ title : String
, content : Html msg
, hasDashedBorder : Bool
, modifyRow : RowModifier
, palette : Palette.Palette
}
{-| This type also is used to give nice errors when passing invalid items to the rows field
-}
type OutlineLayout msg
= Row (CustomConfig msg)
| KeyedRow String (CustomConfig msg)
{-| -}
type RowModifier
= NodeEvaluated Evaluation
| Normal
{-| The vertical alignment of the outline horizontal bar
-}
type RowVerticalAlignment
= TopAlign
| MiddleAlign
{-| -}
type Evaluation
= Good
| Bad
{-| -}
type Hierarchy
= Root
| Child

View File

@ -1,392 +0,0 @@
module Nri.Outline.Utils exposing (..)
{-|
@docs RowNodeType, keyedRowToHtml, outlinePanel, paletteOfFirst, rawRow, rawRowWithExtraContent, rowToHtml, toHtml, toKeyedHtml, toRowNodeType, viewExtraContent, viewRow, withEvaluated, withTitle
-}
import Css exposing (..)
import Html exposing (..)
import Html.Attributes
import Html.Keyed
import Nri.Outline.Styles as Styles exposing (styles)
import Nri.Outline.Types as Types
import Nri.Palette as Palette
{-| -}
type RowNodeType msg
= Tag (Html msg)
| KeyedTag ( String, Html msg )
{-| -}
withTitle : String -> Types.OutlineLayout msg -> Types.OutlineLayout msg
withTitle title taggedRow =
let
updatedConfig config =
{ config
| content =
outlinePanel
{ title = title
, content = config.content
, hasDashedBorder = config.hasDashedBorder
, palette = config.palette
, modifyRow = config.modifyRow
}
, verticalAlign = Types.TopAlign
}
in
case taggedRow of
Types.Row config ->
Types.Row <| updatedConfig config
Types.KeyedRow key config ->
Types.KeyedRow key <| updatedConfig config
{-| -}
withEvaluated : Types.Evaluation -> Types.OutlineLayout msg -> Types.OutlineLayout msg
withEvaluated evaluation taggedRow =
{- TODO: this should render with styles like a row with title, but hide the title.
Currently, the developer and use this without a title. If that happens, then
this has no affect on the row
-}
case taggedRow of
Types.Row config ->
Types.Row { config | modifyRow = Types.NodeEvaluated evaluation }
Types.KeyedRow key config ->
Types.KeyedRow key { config | modifyRow = Types.NodeEvaluated evaluation }
{-| -}
outlinePanel : Types.OutlinePanelConfig msg -> Html msg
outlinePanel config =
div []
[ div
[ styles.class
[ Styles.RowPanelTitle
, case config.palette.name of
Palette.Gray ->
Styles.RowPanelTitleGray
Palette.DarkGray ->
Styles.RowPanelTitleDarkGray
Palette.Blue ->
Styles.RowPanelTitleBlue
Palette.DarkBlue ->
Styles.RowPanelTitleDarkBlue
Palette.Purple ->
Styles.RowPanelTitlePurple
Palette.Turquoise ->
Styles.RowPanelTitleTurquoise
Palette.Red ->
Styles.RowPanelTitleRed
Palette.Green ->
Styles.RowPanelTitleGreen
Palette.White ->
Styles.RowPanelTitleWhite
Palette.Cornflower ->
Styles.RowPanelTitleCornflower
Palette.Aqua ->
Styles.RowPanelTitleAqua
]
]
[ Html.text config.title ]
, div
[ styles.classList
[ ( Styles.RowPanelContent, True )
, ( Styles.RowPanelContentDashed, config.hasDashedBorder )
, ( case config.modifyRow of
Types.NodeEvaluated Types.Good ->
Styles.RowPanelContentGreen
Types.NodeEvaluated Types.Bad ->
Styles.RowPanelContentPurple
_ ->
case config.palette.name of
Palette.Gray ->
Styles.RowPanelContentGray
Palette.DarkGray ->
Styles.RowPanelContentDarkGray
Palette.Blue ->
Styles.RowPanelContentBlue
Palette.DarkBlue ->
Styles.RowPanelContentDarkBlue
Palette.Purple ->
Styles.RowPanelContentPurple
Palette.Turquoise ->
Styles.RowPanelContentTurquoise
Palette.Red ->
Styles.RowPanelContentRed
Palette.Green ->
Styles.RowPanelContentGreen
Palette.White ->
Styles.RowPanelContentWhite
Palette.Cornflower ->
Styles.RowPanelContentCornflower
Palette.Aqua ->
Styles.RowPanelContentAqua
, True
)
]
]
[ config.content ]
]
{-| -}
toRowNodeType : Types.Hierarchy -> Types.OutlineLayout msg -> RowNodeType msg
toRowNodeType hierarchy layout =
case layout of
Types.Row config ->
Tag <|
viewRow
hierarchy
config
(ul
[ styles.class [ Styles.RowChildren ] ]
(List.map (rowToHtml Types.Child) config.rows)
)
Types.KeyedRow key config ->
KeyedTag
( key
, viewRow
hierarchy
config
(Html.Keyed.node "ul"
[ styles.class [ Styles.RowChildren ] ]
(List.map (keyedRowToHtml Types.Child) config.rows)
)
)
{-| -}
viewRow : Types.Hierarchy -> Types.CustomConfig msg -> Html msg -> Html msg
viewRow hierarchy config children =
let
extraContentPalette =
paletteOfFirst config.rows
rowAttrs =
[ styles.classList
[ ( Styles.Row, True )
, ( Styles.ChildRow, hierarchy == Types.Child )
, ( case config.palette.name of
Palette.Blue ->
Styles.RowBlue
Palette.Gray ->
Styles.RowGray
Palette.DarkGray ->
Styles.RowDarkGray
Palette.DarkBlue ->
Styles.RowDarkBlue
Palette.Purple ->
Styles.RowPurple
Palette.Turquoise ->
Styles.RowTurquoise
Palette.Red ->
Styles.RowRed
Palette.Green ->
Styles.RowGreen
Palette.White ->
Styles.RowWhite
Palette.Cornflower ->
Styles.RowCornflower
Palette.Aqua ->
Styles.RowAqua
, True
)
]
-- , OutlineCss.modifierClassFromAlign config.verticalAlign
]
in
li
rowAttrs
[ div
[ styles.classList
[ ( Styles.RowContent, True )
, ( Styles.ChildRowContent, hierarchy == Types.Child )
, ( case config.palette.name of
Palette.Blue ->
Styles.ChildRowContentBlue
Palette.Gray ->
Styles.ChildRowContentGray
Palette.DarkGray ->
Styles.ChildRowContentDarkGray
Palette.DarkBlue ->
Styles.ChildRowContentDarkBlue
Palette.Purple ->
Styles.ChildRowContentPurple
Palette.Turquoise ->
Styles.ChildRowContentTurquoise
Palette.Red ->
Styles.ChildRowContentRed
Palette.Green ->
Styles.ChildRowContentGreen
Palette.White ->
Styles.ChildRowContentWhite
Palette.Cornflower ->
Styles.ChildRowContentCornflower
Palette.Aqua ->
Styles.ChildRowContentAqua
, True
)
]
]
[ config.content
, Maybe.map (viewExtraContent extraContentPalette) config.extraContent
|> Maybe.withDefault (Html.text "")
]
, if List.isEmpty config.rows then
Html.text ""
else
children
]
{-| -}
viewExtraContent : Maybe Palette.Palette -> Html msg -> Html msg
viewExtraContent palette content =
let
{- Styles are inline here to accomodate the calculations we must do
to correctly format extra content. This is based off if there are any child rows
following the extra content. If there are, we add the padding and border styles.
If there are not, we have no styling.
-}
styles =
(Css.asPairs >> Html.Attributes.style) <|
case palette of
Just { border } ->
[ marginLeft (px 15)
, borderLeft3 (px 1) solid border
, paddingLeft (px 15)
]
Nothing ->
[]
in
div [ styles ] [ content ]
{-| When the row has extra content, the palette from the first child needs to be known in
order to have the correct border on the left of the extra content. This ensures we have
a consistent styling on the outline borders.
-}
paletteOfFirst : List (Types.OutlineLayout msg) -> Maybe Palette.Palette
paletteOfFirst rows =
rows
|> List.head
|> Maybe.map
(\row ->
case row of
Types.Row config ->
config.palette
Types.KeyedRow _ config ->
config.palette
)
{-| -}
rowToHtml : Types.Hierarchy -> Types.OutlineLayout msg -> Html msg
rowToHtml hierarchy =
toRowNodeType hierarchy >> toHtml
{-| -}
keyedRowToHtml : Types.Hierarchy -> Types.OutlineLayout msg -> ( String, Html msg )
keyedRowToHtml hierarchy =
toRowNodeType hierarchy >> toKeyedHtml
{-| -}
toHtml : RowNodeType msg -> Html msg
toHtml row =
case row of
Tag html ->
html
KeyedTag ( _, html ) ->
html
{-| -}
toKeyedHtml : RowNodeType msg -> ( String, Html msg )
toKeyedHtml row =
case row of
Tag html ->
( "", html )
KeyedTag output ->
output
{-| -}
rawRow : Types.RowConfig msg -> Types.CustomConfig msg
rawRow config =
{ content = config.content
, palette = config.palette
, rows = config.rows
, verticalAlign = Types.MiddleAlign
, extraContent = Nothing
, modifyRow = Types.Normal
, hasDashedBorder = False
}
{-| -}
rawRowWithExtraContent : Types.ExtraContenRowConfig msg -> Types.CustomConfig msg
rawRowWithExtraContent config =
{ content = config.content
, palette = config.palette
, rows = config.rows
, verticalAlign = Types.TopAlign
, extraContent = Just config.extraContent
, modifyRow = Types.Normal
, hasDashedBorder = False
}