mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2025-01-08 07:27:44 +03:00
Merge remote-tracking branch 'origin/master' into bat/hover-highlight-styles
This commit is contained in:
commit
c7980f5ebf
@ -19,7 +19,7 @@ import Nri.Ui.AnimatedIcon.V1 as AnimatedIcon
|
|||||||
import Nri.Ui.Colors.V1 as Colors
|
import Nri.Ui.Colors.V1 as Colors
|
||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Svg.V1 as Svg
|
import Nri.Ui.Svg.V1 as Svg
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
|
|
||||||
|
|
||||||
moduleName : String
|
moduleName : String
|
||||||
@ -88,7 +88,7 @@ example =
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
, Heading.h2 [ Heading.plaintext "Example" ]
|
, Heading.h2 [ Heading.plaintext "Example" ]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.custom
|
[ Table.custom
|
||||||
{ header = text "Rendered"
|
{ header = text "Rendered"
|
||||||
, view =
|
, view =
|
||||||
|
@ -26,7 +26,7 @@ import Nri.Ui.ClickableText.V3 as ClickableText
|
|||||||
import Nri.Ui.Fonts.V1 as Fonts
|
import Nri.Ui.Fonts.V1 as Fonts
|
||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Spacing.V1 as Spacing
|
import Nri.Ui.Spacing.V1 as Spacing
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Nri.Ui.Text.V6 as Text
|
import Nri.Ui.Text.V6 as Text
|
||||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||||
import Task
|
import Task
|
||||||
@ -251,7 +251,7 @@ example =
|
|||||||
, Block.labelPosition (Dict.get longId offsets)
|
, Block.labelPosition (Dict.get longId offsets)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.custom
|
[ Table.custom
|
||||||
{ header = text "Pattern name & description"
|
{ header = text "Pattern name & description"
|
||||||
, view = .description >> Markdown.toHtml Nothing >> List.map fromUnstyled >> div []
|
, view = .description >> Markdown.toHtml Nothing >> List.map fromUnstyled >> div []
|
||||||
|
@ -23,7 +23,7 @@ import Nri.Ui.Fonts.V1 as Fonts
|
|||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Html.V3 exposing (viewJust)
|
import Nri.Ui.Html.V3 exposing (viewJust)
|
||||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||||
|
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ example =
|
|||||||
[ Heading.h2 [ Heading.plaintext "viewSecondary Example" ]
|
[ Heading.h2 [ Heading.plaintext "viewSecondary Example" ]
|
||||||
, viewJust (Tuple.second >> viewSecondaryExample settings.currentRoute) breadCrumbs
|
, viewJust (Tuple.second >> viewSecondaryExample settings.currentRoute) breadCrumbs
|
||||||
]
|
]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "Name"
|
{ header = "Name"
|
||||||
, value = .name
|
, value = .name
|
||||||
|
@ -21,7 +21,7 @@ import Nri.Ui.Colors.V1 as Colors
|
|||||||
import Nri.Ui.Fonts.V1 as Fonts
|
import Nri.Ui.Fonts.V1 as Fonts
|
||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Svg.V1 as Svg
|
import Nri.Ui.Svg.V1 as Svg
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
|
|
||||||
|
|
||||||
moduleName : String
|
moduleName : String
|
||||||
@ -66,7 +66,7 @@ example =
|
|||||||
, Heading.h2 [ Heading.plaintext "Customizable example" ]
|
, Heading.h2 [ Heading.plaintext "Customizable example" ]
|
||||||
, exampleView
|
, exampleView
|
||||||
, Heading.h2 [ Heading.plaintext "Examples" ]
|
, Heading.h2 [ Heading.plaintext "Examples" ]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "State"
|
{ header = "State"
|
||||||
, value = .state
|
, value = .state
|
||||||
|
@ -13,7 +13,7 @@ import Html.Styled as Html exposing (Html)
|
|||||||
import Html.Styled.Attributes exposing (css)
|
import Html.Styled.Attributes exposing (css)
|
||||||
import Nri.Ui.ClickableText.V3 as ClickableText
|
import Nri.Ui.ClickableText.V3 as ClickableText
|
||||||
import Nri.Ui.Fonts.V1 as Fonts
|
import Nri.Ui.Fonts.V1 as Fonts
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Nri.Ui.Text.V6 as Text
|
import Nri.Ui.Text.V6 as Text
|
||||||
|
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ viewFontFailurePatterns =
|
|||||||
, Css.textAlign Css.center
|
, Css.textAlign Css.center
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
Table.view
|
Table.view []
|
||||||
[ Table.rowHeader
|
[ Table.rowHeader
|
||||||
{ header = Html.text "Example"
|
{ header = Html.text "Example"
|
||||||
, view = Html.text << .example
|
, view = Html.text << .example
|
||||||
|
@ -25,7 +25,7 @@ import Nri.Ui.Heading.V3 as Heading
|
|||||||
import Nri.Ui.Highlightable.V3 as Highlightable exposing (Highlightable)
|
import Nri.Ui.Highlightable.V3 as Highlightable exposing (Highlightable)
|
||||||
import Nri.Ui.Highlighter.V4 as Highlighter
|
import Nri.Ui.Highlighter.V4 as Highlighter
|
||||||
import Nri.Ui.HighlighterTool.V1 as Tool
|
import Nri.Ui.HighlighterTool.V1 as Tool
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Sort exposing (Sorter)
|
import Sort exposing (Sorter)
|
||||||
import String.Extra
|
import String.Extra
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ example =
|
|||||||
]
|
]
|
||||||
, Heading.h2 [ Heading.plaintext "Non-interactive examples" ]
|
, Heading.h2 [ Heading.plaintext "Non-interactive examples" ]
|
||||||
, Heading.h3 [ Heading.plaintext "These are examples of some different ways the highlighter can appear to users." ]
|
, Heading.h3 [ Heading.plaintext "These are examples of some different ways the highlighter can appear to users." ]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.rowHeader
|
[ Table.rowHeader
|
||||||
{ header = text "Highlighter."
|
{ header = text "Highlighter."
|
||||||
, view = .viewName >> text
|
, view = .viewName >> text
|
||||||
|
@ -29,7 +29,7 @@ import Nri.Ui.Fonts.V1 as Fonts
|
|||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Menu.V4 as Menu
|
import Nri.Ui.Menu.V4 as Menu
|
||||||
import Nri.Ui.Spacing.V1 as Spacing
|
import Nri.Ui.Spacing.V1 as Spacing
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Nri.Ui.TextInput.V7 as TextInput
|
import Nri.Ui.TextInput.V7 as TextInput
|
||||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||||
@ -230,7 +230,7 @@ view ellieLinkConfig state =
|
|||||||
[ Heading.plaintext "Menu types"
|
[ Heading.plaintext "Menu types"
|
||||||
, Heading.css [ Css.margin2 Spacing.verticalSpacerPx Css.zero ]
|
, Heading.css [ Css.margin2 Spacing.verticalSpacerPx Css.zero ]
|
||||||
]
|
]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "Menu type"
|
{ header = "Menu type"
|
||||||
, value = .menu
|
, value = .menu
|
||||||
|
@ -33,7 +33,7 @@ import Nri.Ui.MediaQuery.V1 exposing (..)
|
|||||||
import Nri.Ui.QuestionBox.V4 as QuestionBox
|
import Nri.Ui.QuestionBox.V4 as QuestionBox
|
||||||
import Nri.Ui.Spacing.V1 as Spacing
|
import Nri.Ui.Spacing.V1 as Spacing
|
||||||
import Nri.Ui.Svg.V1 as Svg
|
import Nri.Ui.Svg.V1 as Svg
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Nri.Ui.Text.V6 as Text
|
import Nri.Ui.Text.V6 as Text
|
||||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||||
import Task
|
import Task
|
||||||
@ -238,7 +238,7 @@ While these visions did appear.
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.custom
|
[ Table.custom
|
||||||
{ header = text "Pattern name & description"
|
{ header = text "Pattern name & description"
|
||||||
, view = .description >> Markdown.toHtml Nothing >> List.map fromUnstyled >> div []
|
, view = .description >> Markdown.toHtml Nothing >> List.map fromUnstyled >> div []
|
||||||
|
@ -20,7 +20,7 @@ import Nri.Ui.Colors.V1 as Colors
|
|||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.RingGauge.V1 as RingGauge
|
import Nri.Ui.RingGauge.V1 as RingGauge
|
||||||
import Nri.Ui.Svg.V1 as Svg
|
import Nri.Ui.Svg.V1 as Svg
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Round
|
import Round
|
||||||
import SolidColor.Accessibility
|
import SolidColor.Accessibility
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ example =
|
|||||||
|> Svg.withWidth (Css.px 200)
|
|> Svg.withWidth (Css.px 200)
|
||||||
|> Svg.withHeight (Css.px 200)
|
|> Svg.withHeight (Css.px 200)
|
||||||
|> Svg.toHtml
|
|> Svg.toHtml
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "Color contrast against"
|
{ header = "Color contrast against"
|
||||||
, value = .name
|
, value = .name
|
||||||
|
@ -10,16 +10,16 @@ import Category exposing (Category(..))
|
|||||||
import Code
|
import Code
|
||||||
import Css exposing (..)
|
import Css exposing (..)
|
||||||
import Debug.Control as Control exposing (Control)
|
import Debug.Control as Control exposing (Control)
|
||||||
import Debug.Control.Extra as ControlExtra
|
import Debug.Control.Extra as ControlExtra exposing (values)
|
||||||
import Debug.Control.View as ControlView
|
import Debug.Control.View as ControlView
|
||||||
import Example exposing (Example)
|
import Example exposing (Example)
|
||||||
import Html.Styled as Html exposing (..)
|
import Html.Styled as Html exposing (..)
|
||||||
import Html.Styled.Attributes exposing (css)
|
import Html.Styled.Attributes exposing (css)
|
||||||
import Nri.Ui.Colors.V1 as Colors
|
import Nri.Ui.Colors.V1 as Colors
|
||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.SortableTable.V3 as SortableTable exposing (Column)
|
import Nri.Ui.SortableTable.V4 as SortableTable exposing (Column)
|
||||||
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ moduleName =
|
|||||||
|
|
||||||
version : Int
|
version : Int
|
||||||
version =
|
version =
|
||||||
3
|
4
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
{-| -}
|
||||||
@ -74,7 +74,7 @@ example =
|
|||||||
|> Svg.withHeight (Css.px 12)
|
|> Svg.withHeight (Css.px 12)
|
||||||
|> Svg.toHtml
|
|> Svg.toHtml
|
||||||
in
|
in
|
||||||
[ Table.view
|
[ Table.view []
|
||||||
[ Table.custom
|
[ Table.custom
|
||||||
{ header = header "X"
|
{ header = header "X"
|
||||||
, view = .x >> Html.text
|
, view = .x >> Html.text
|
||||||
@ -104,10 +104,24 @@ example =
|
|||||||
settings =
|
settings =
|
||||||
Control.currentValue model.settings
|
Control.currentValue model.settings
|
||||||
|
|
||||||
config =
|
attrs =
|
||||||
{ updateMsg = SetSortState
|
List.filterMap identity
|
||||||
, columns = columns
|
[ Just (SortableTable.updateMsg SetSortState)
|
||||||
}
|
, Just (SortableTable.state sortState)
|
||||||
|
, Maybe.map
|
||||||
|
(\stickiness ->
|
||||||
|
case stickiness of
|
||||||
|
Default ->
|
||||||
|
SortableTable.stickyHeader
|
||||||
|
|
||||||
|
Custom customConfig ->
|
||||||
|
SortableTable.stickyHeaderCustom customConfig
|
||||||
|
)
|
||||||
|
settings.stickyHeader
|
||||||
|
]
|
||||||
|
|
||||||
|
isStickyAtAll =
|
||||||
|
settings.stickyHeader /= Nothing
|
||||||
|
|
||||||
( dataCode, data ) =
|
( dataCode, data ) =
|
||||||
List.unzip dataWithCode
|
List.unzip dataWithCode
|
||||||
@ -119,15 +133,35 @@ example =
|
|||||||
{ sectionName = viewName
|
{ sectionName = viewName
|
||||||
, code =
|
, code =
|
||||||
[ moduleName ++ "." ++ viewName
|
[ moduleName ++ "." ++ viewName
|
||||||
, Code.recordMultiline
|
, Code.listMultiline
|
||||||
[ ( "updateMsg", "SetSortState" )
|
(List.filterMap identity
|
||||||
, ( "columns", Code.listMultiline columnsCode 2 )
|
[ Just "SortableTable.updateMsg SetSortState"
|
||||||
]
|
, "-- The SortableTable's state should be stored on the model, rather than initialized in the view"
|
||||||
|
++ "\n "
|
||||||
|
++ "SortableTable.state (SortableTable.init "
|
||||||
|
++ Debug.toString model.sortState.column
|
||||||
|
++ ")"
|
||||||
|
|> Just
|
||||||
|
, Maybe.map
|
||||||
|
(\stickiness ->
|
||||||
|
case stickiness of
|
||||||
|
Default ->
|
||||||
|
"SortableTable.stickyHeader"
|
||||||
|
|
||||||
|
Custom stickyConfig ->
|
||||||
|
"SortableTable.stickyHeaderCustom "
|
||||||
|
++ Code.recordMultiline
|
||||||
|
[ ( "topOffset", String.fromFloat stickyConfig.topOffset )
|
||||||
|
, ( "zIndex", String.fromInt stickyConfig.zIndex )
|
||||||
|
, ( "pageBackgroundColor", "Css.hex \"" ++ stickyConfig.pageBackgroundColor.value ++ "\"" )
|
||||||
|
]
|
||||||
|
2
|
||||||
|
)
|
||||||
|
settings.stickyHeader
|
||||||
|
]
|
||||||
|
)
|
||||||
1
|
1
|
||||||
, Code.newlineWithIndent 1
|
, Code.listMultiline columnsCode 1
|
||||||
, Code.commentInline "The SortableTable's state should be stored on the model, rather than initialized in the view"
|
|
||||||
, Code.newlineWithIndent 1
|
|
||||||
, Code.withParens ("SortableTable.init " ++ Debug.toString model.sortState.column)
|
|
||||||
, finalArgs
|
, finalArgs
|
||||||
]
|
]
|
||||||
|> String.join ""
|
|> String.join ""
|
||||||
@ -149,10 +183,19 @@ example =
|
|||||||
}
|
}
|
||||||
, Heading.h2 [ Heading.plaintext "Example" ]
|
, Heading.h2 [ Heading.plaintext "Example" ]
|
||||||
, if settings.loading then
|
, if settings.loading then
|
||||||
SortableTable.viewLoading config sortState
|
SortableTable.viewLoading attrs columns
|
||||||
|
|
||||||
else
|
else
|
||||||
SortableTable.view config sortState data
|
SortableTable.view attrs
|
||||||
|
columns
|
||||||
|
(if isStickyAtAll then
|
||||||
|
data
|
||||||
|
|> List.repeat 10
|
||||||
|
|> List.concat
|
||||||
|
|
||||||
|
else
|
||||||
|
data
|
||||||
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,9 +317,15 @@ type alias Settings =
|
|||||||
, customizableColumnWidth : Int
|
, customizableColumnWidth : Int
|
||||||
, customizableColumnCellStyles : ( String, List Style )
|
, customizableColumnCellStyles : ( String, List Style )
|
||||||
, loading : Bool
|
, loading : Bool
|
||||||
|
, stickyHeader : Maybe StickyHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type StickyHeader
|
||||||
|
= Default
|
||||||
|
| Custom SortableTable.StickyConfig
|
||||||
|
|
||||||
|
|
||||||
controlSettings : Control Settings
|
controlSettings : Control Settings
|
||||||
controlSettings =
|
controlSettings =
|
||||||
Control.record Settings
|
Control.record Settings
|
||||||
@ -295,6 +344,25 @@ controlSettings =
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> Control.field "Is loading" (Control.bool False)
|
|> Control.field "Is loading" (Control.bool False)
|
||||||
|
|> Control.field "Sticky header"
|
||||||
|
(Control.maybe False
|
||||||
|
(Control.choice
|
||||||
|
[ ( "Default", Control.value Default )
|
||||||
|
, ( "Custom"
|
||||||
|
, Control.record SortableTable.StickyConfig
|
||||||
|
|> Control.field "topOffset" (values String.fromFloat [ 0, 10, 50 ])
|
||||||
|
|> Control.field "zIndex" (values String.fromInt [ 0, 1, 5, 10 ])
|
||||||
|
|> Control.field "pageBackgroundColor"
|
||||||
|
(Control.choice
|
||||||
|
[ ( "white", Control.value Colors.white )
|
||||||
|
, ( "gray", Control.value Colors.gray92 )
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> Control.map Custom
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
type ColumnId
|
type ColumnId
|
||||||
|
@ -21,7 +21,7 @@ import Nri.Ui.Container.V2 as Container
|
|||||||
import Nri.Ui.Fonts.V1 as Fonts
|
import Nri.Ui.Fonts.V1 as Fonts
|
||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Spacing.V1 as Spacing
|
import Nri.Ui.Spacing.V1 as Spacing
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Svg.Styled
|
import Svg.Styled
|
||||||
import Svg.Styled.Attributes
|
import Svg.Styled.Attributes
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ view ellieLinkConfig state =
|
|||||||
, Heading.h2 [ Heading.plaintext "Example", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
|
, Heading.h2 [ Heading.plaintext "Example", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
|
||||||
, fakePage [ exampleView ]
|
, fakePage [ exampleView ]
|
||||||
, Heading.h2 [ Heading.plaintext "Content alignment", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
|
, Heading.h2 [ Heading.plaintext "Content alignment", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "Name"
|
{ header = "Name"
|
||||||
, value = .name
|
, value = .name
|
||||||
@ -275,7 +275,7 @@ view ellieLinkConfig state =
|
|||||||
, { name = "centeredContentWithCustomWidth", alignment = "Centered", maxWidth = "(customizable)", sidePadding = "0px" }
|
, { name = "centeredContentWithCustomWidth", alignment = "Centered", maxWidth = "(customizable)", sidePadding = "0px" }
|
||||||
]
|
]
|
||||||
, Heading.h2 [ Heading.plaintext "Constants", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
|
, Heading.h2 [ Heading.plaintext "Constants", Heading.css [ Css.marginTop Spacing.verticalSpacerPx ] ]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "Name"
|
{ header = "Name"
|
||||||
, value = .name
|
, value = .name
|
||||||
|
@ -15,7 +15,7 @@ import Debug.Control.View as ControlView
|
|||||||
import Example exposing (Example)
|
import Example exposing (Example)
|
||||||
import Nri.Ui.Button.V10 as Button
|
import Nri.Ui.Button.V10 as Button
|
||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Table.V6 as Table exposing (Column)
|
import Nri.Ui.Table.V7 as Table exposing (Column)
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
{-| -}
|
||||||
@ -30,7 +30,7 @@ moduleName =
|
|||||||
|
|
||||||
version : Int
|
version : Int
|
||||||
version =
|
version =
|
||||||
6
|
7
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
{-| -}
|
||||||
@ -44,7 +44,7 @@ example =
|
|||||||
, categories = [ Layout ]
|
, categories = [ Layout ]
|
||||||
, keyboardSupport = []
|
, keyboardSupport = []
|
||||||
, preview =
|
, preview =
|
||||||
[ Table.view
|
[ Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "A"
|
{ header = "A"
|
||||||
, value = .a
|
, value = .a
|
||||||
@ -98,6 +98,7 @@ example =
|
|||||||
{ sectionName = moduleName ++ "." ++ viewName
|
{ sectionName = moduleName ++ "." ++ viewName
|
||||||
, code =
|
, code =
|
||||||
(moduleName ++ "." ++ viewName)
|
(moduleName ++ "." ++ viewName)
|
||||||
|
++ " [] "
|
||||||
++ Code.list columnsCode
|
++ Code.list columnsCode
|
||||||
++ dataStr
|
++ dataStr
|
||||||
}
|
}
|
||||||
@ -111,16 +112,16 @@ example =
|
|||||||
, Heading.h2 [ Heading.plaintext "Example" ]
|
, Heading.h2 [ Heading.plaintext "Example" ]
|
||||||
, case ( showHeader, isLoading ) of
|
, case ( showHeader, isLoading ) of
|
||||||
( True, False ) ->
|
( True, False ) ->
|
||||||
Table.view columns data
|
Table.view [] columns data
|
||||||
|
|
||||||
( False, False ) ->
|
( False, False ) ->
|
||||||
Table.viewWithoutHeader columns data
|
Table.viewWithoutHeader [] columns data
|
||||||
|
|
||||||
( True, True ) ->
|
( True, True ) ->
|
||||||
Table.viewLoading columns
|
Table.viewLoading [] columns
|
||||||
|
|
||||||
( False, True ) ->
|
( False, True ) ->
|
||||||
Table.viewLoadingWithoutHeader columns
|
Table.viewLoadingWithoutHeader [] columns
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import Nri.Ui.ClickableText.V3 as ClickableText
|
|||||||
import Nri.Ui.Colors.V1 as Colors
|
import Nri.Ui.Colors.V1 as Colors
|
||||||
import Nri.Ui.Heading.V3 as Heading
|
import Nri.Ui.Heading.V3 as Heading
|
||||||
import Nri.Ui.Svg.V1 as Svg
|
import Nri.Ui.Svg.V1 as Svg
|
||||||
import Nri.Ui.Table.V6 as Table
|
import Nri.Ui.Table.V7 as Table
|
||||||
import Nri.Ui.Tooltip.V3 as Tooltip
|
import Nri.Ui.Tooltip.V3 as Tooltip
|
||||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ view : EllieLink.Config -> State -> List (Html Msg)
|
|||||||
view ellieLinkConfig model =
|
view ellieLinkConfig model =
|
||||||
[ viewCustomizableExample ellieLinkConfig model.staticExampleSettings
|
[ viewCustomizableExample ellieLinkConfig model.staticExampleSettings
|
||||||
, Heading.h2 [ Heading.plaintext "What type of tooltip should I use?" ]
|
, Heading.h2 [ Heading.plaintext "What type of tooltip should I use?" ]
|
||||||
, Table.view
|
, Table.view []
|
||||||
[ Table.string
|
[ Table.string
|
||||||
{ header = "Type"
|
{ header = "Type"
|
||||||
, value = .name
|
, value = .name
|
||||||
|
@ -8,5 +8,7 @@ Nri.Ui.HighlighterToolbar.V2,upgrade to V3
|
|||||||
Nri.Ui.QuestionBox.V2,upgrade to V4
|
Nri.Ui.QuestionBox.V2,upgrade to V4
|
||||||
Nri.Ui.QuestionBox.V3,upgrade to V4
|
Nri.Ui.QuestionBox.V3,upgrade to V4
|
||||||
Nri.Ui.Select.V8,upgrade to V9
|
Nri.Ui.Select.V8,upgrade to V9
|
||||||
|
Nri.Ui.SortableTable.V3,upgrade to V4
|
||||||
|
Nri.Ui.Table.V6,upgrade to V7
|
||||||
Nri.Ui.Tabs.V6,upgrade to V8
|
Nri.Ui.Tabs.V6,upgrade to V8
|
||||||
Nri.Ui.Tabs.V7,upgrade to V8
|
Nri.Ui.Tabs.V7,upgrade to V8
|
||||||
|
|
6
elm.json
6
elm.json
@ -3,7 +3,7 @@
|
|||||||
"name": "NoRedInk/noredink-ui",
|
"name": "NoRedInk/noredink-ui",
|
||||||
"summary": "UI Widgets we use at NRI",
|
"summary": "UI Widgets we use at NRI",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"version": "22.7.0",
|
"version": "22.8.0",
|
||||||
"exposed-modules": [
|
"exposed-modules": [
|
||||||
"Browser.Events.Extra",
|
"Browser.Events.Extra",
|
||||||
"Nri.Ui",
|
"Nri.Ui",
|
||||||
@ -72,11 +72,13 @@
|
|||||||
"Nri.Ui.Shadows.V1",
|
"Nri.Ui.Shadows.V1",
|
||||||
"Nri.Ui.SideNav.V4",
|
"Nri.Ui.SideNav.V4",
|
||||||
"Nri.Ui.SortableTable.V3",
|
"Nri.Ui.SortableTable.V3",
|
||||||
|
"Nri.Ui.SortableTable.V4",
|
||||||
"Nri.Ui.Spacing.V1",
|
"Nri.Ui.Spacing.V1",
|
||||||
"Nri.Ui.Sprite.V1",
|
"Nri.Ui.Sprite.V1",
|
||||||
"Nri.Ui.Svg.V1",
|
"Nri.Ui.Svg.V1",
|
||||||
"Nri.Ui.Switch.V2",
|
"Nri.Ui.Switch.V2",
|
||||||
"Nri.Ui.Table.V6",
|
"Nri.Ui.Table.V6",
|
||||||
|
"Nri.Ui.Table.V7",
|
||||||
"Nri.Ui.Tabs.V6",
|
"Nri.Ui.Tabs.V6",
|
||||||
"Nri.Ui.Tabs.V7",
|
"Nri.Ui.Tabs.V7",
|
||||||
"Nri.Ui.Tabs.V8",
|
"Nri.Ui.Tabs.V8",
|
||||||
@ -116,4 +118,4 @@
|
|||||||
"elm-explorations/test": "1.2.2 <= v < 2.0.0",
|
"elm-explorations/test": "1.2.2 <= v < 2.0.0",
|
||||||
"tesk9/accessible-html": "5.0.0 <= v < 6.0.0"
|
"tesk9/accessible-html": "5.0.0 <= v < 6.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -195,6 +195,9 @@ hint = 'upgrade to V4'
|
|||||||
[forbidden."Nri.Ui.SortableTable.V2"]
|
[forbidden."Nri.Ui.SortableTable.V2"]
|
||||||
hint = 'upgrade to V3'
|
hint = 'upgrade to V3'
|
||||||
|
|
||||||
|
[forbidden."Nri.Ui.SortableTable.V3"]
|
||||||
|
hint = 'upgrade to V4'
|
||||||
|
|
||||||
[forbidden."Nri.Ui.Switch.V1"]
|
[forbidden."Nri.Ui.Switch.V1"]
|
||||||
hint = 'upgrade to V2'
|
hint = 'upgrade to V2'
|
||||||
|
|
||||||
@ -204,6 +207,9 @@ hint = 'upgrade to V5'
|
|||||||
[forbidden."Nri.Ui.Table.V5"]
|
[forbidden."Nri.Ui.Table.V5"]
|
||||||
hint = 'upgrade to V6'
|
hint = 'upgrade to V6'
|
||||||
|
|
||||||
|
[forbidden."Nri.Ui.Table.V6"]
|
||||||
|
hint = 'upgrade to V7'
|
||||||
|
|
||||||
[forbidden."Nri.Ui.Tabs.V6"]
|
[forbidden."Nri.Ui.Tabs.V6"]
|
||||||
hint = 'upgrade to V8'
|
hint = 'upgrade to V8'
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ view { label, name, value, valueToString, selectedValue } attributes =
|
|||||||
, css
|
, css
|
||||||
[ position relative
|
[ position relative
|
||||||
, Css.marginLeft (Css.px -2)
|
, Css.marginLeft (Css.px -2)
|
||||||
, Css.paddingLeft (Css.px 38)
|
, Css.paddingLeft (Css.px 30)
|
||||||
, Css.paddingTop (px 6)
|
, Css.paddingTop (px 6)
|
||||||
, Css.paddingBottom (px 4)
|
, Css.paddingBottom (px 4)
|
||||||
, display inlineBlock
|
, display inlineBlock
|
||||||
@ -383,6 +383,7 @@ view { label, name, value, valueToString, selectedValue } attributes =
|
|||||||
, Css.property "font-weight" "600"
|
, Css.property "font-weight" "600"
|
||||||
, display inlineBlock
|
, display inlineBlock
|
||||||
, Css.property "transition" "all 0.4s ease"
|
, Css.property "transition" "all 0.4s ease"
|
||||||
|
, paddingLeft (px 8)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[ radioInputIcon
|
[ radioInputIcon
|
||||||
@ -555,9 +556,6 @@ radioInputIcon config =
|
|||||||
, displayFlex
|
, displayFlex
|
||||||
, justifyContent center
|
, justifyContent center
|
||||||
, alignItems center
|
, alignItems center
|
||||||
, -- this padding creates a hit area "bridge" between the
|
|
||||||
-- absolutely-positioned icon SVG and the label text
|
|
||||||
paddingRight (Css.px 8)
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[ image
|
[ image
|
||||||
|
@ -32,7 +32,7 @@ import Nri.Ui.Colors.V1
|
|||||||
import Nri.Ui.CssVendorPrefix.V1 as CssVendorPrefix
|
import Nri.Ui.CssVendorPrefix.V1 as CssVendorPrefix
|
||||||
import Nri.Ui.Fonts.V1 as Fonts
|
import Nri.Ui.Fonts.V1 as Fonts
|
||||||
import Nri.Ui.Svg.V1
|
import Nri.Ui.Svg.V1
|
||||||
import Nri.Ui.Table.V6 as Table exposing (SortDirection(..))
|
import Nri.Ui.Table.V7 as Table exposing (SortDirection(..))
|
||||||
import Nri.Ui.UiIcon.V1
|
import Nri.Ui.UiIcon.V1
|
||||||
|
|
||||||
|
|
||||||
@ -189,8 +189,7 @@ viewLoading config state =
|
|||||||
tableColumns =
|
tableColumns =
|
||||||
List.map (buildTableColumn config.updateMsg state) config.columns
|
List.map (buildTableColumn config.updateMsg state) config.columns
|
||||||
in
|
in
|
||||||
Table.viewLoading
|
Table.viewLoading [] tableColumns
|
||||||
tableColumns
|
|
||||||
|
|
||||||
|
|
||||||
{-| -}
|
{-| -}
|
||||||
@ -203,7 +202,7 @@ view config state entries =
|
|||||||
sorter =
|
sorter =
|
||||||
findSorter config.columns state.column
|
findSorter config.columns state.column
|
||||||
in
|
in
|
||||||
Table.view
|
Table.view []
|
||||||
tableColumns
|
tableColumns
|
||||||
(List.sortWith (sorter state.sortDirection) entries)
|
(List.sortWith (sorter state.sortDirection) entries)
|
||||||
|
|
||||||
|
505
src/Nri/Ui/SortableTable/V4.elm
Normal file
505
src/Nri/Ui/SortableTable/V4.elm
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
module Nri.Ui.SortableTable.V4 exposing
|
||||||
|
( Column, Sorter, State
|
||||||
|
, init, initDescending
|
||||||
|
, custom, string
|
||||||
|
, Attribute, updateMsg, state, stickyHeader, stickyHeaderCustom, StickyConfig, view, viewLoading
|
||||||
|
, invariantSort, simpleSort, combineSorters
|
||||||
|
)
|
||||||
|
|
||||||
|
{-| Changes from V3:
|
||||||
|
|
||||||
|
- Change to an HTML-like API
|
||||||
|
- Allow the table header to be sticky
|
||||||
|
|
||||||
|
@docs Column, Sorter, State
|
||||||
|
@docs init, initDescending
|
||||||
|
@docs custom, string
|
||||||
|
@docs Attribute, updateMsg, state, stickyHeader, stickyHeaderCustom, StickyConfig, view, viewLoading
|
||||||
|
@docs invariantSort, simpleSort, combineSorters
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Accessibility.Styled.Aria as Aria
|
||||||
|
import Css exposing (..)
|
||||||
|
import Css.Global
|
||||||
|
import Html.Styled as Html exposing (Html)
|
||||||
|
import Html.Styled.Attributes exposing (css)
|
||||||
|
import Html.Styled.Events
|
||||||
|
import Nri.Ui.Colors.V1
|
||||||
|
import Nri.Ui.CssVendorPrefix.V1 as CssVendorPrefix
|
||||||
|
import Nri.Ui.Fonts.V1 as Fonts
|
||||||
|
import Nri.Ui.Html.Attributes.V2 exposing (maybe)
|
||||||
|
import Nri.Ui.Html.V3 exposing (viewJust)
|
||||||
|
import Nri.Ui.Svg.V1
|
||||||
|
import Nri.Ui.Table.V7 as Table exposing (SortDirection(..))
|
||||||
|
import Nri.Ui.UiIcon.V1
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type alias Sorter a =
|
||||||
|
SortDirection -> a -> a -> Order
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type Column id entry msg
|
||||||
|
= Column
|
||||||
|
{ id : id
|
||||||
|
, header : Html msg
|
||||||
|
, view : entry -> Html msg
|
||||||
|
, sorter : Maybe (Sorter entry)
|
||||||
|
, width : Int
|
||||||
|
, cellStyles : entry -> List Style
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type alias State id =
|
||||||
|
{ column : id
|
||||||
|
, sortDirection : SortDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
type alias Config id msg =
|
||||||
|
{ updateMsg : Maybe (State id -> msg)
|
||||||
|
, state : Maybe (State id)
|
||||||
|
, stickyHeader : Maybe StickyConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defaultConfig : Config id msg
|
||||||
|
defaultConfig =
|
||||||
|
{ updateMsg = Nothing
|
||||||
|
, state = Nothing
|
||||||
|
, stickyHeader = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| How the header will be set up to be sticky.
|
||||||
|
|
||||||
|
- `topOffset` controls how far off the top of the viewport the headers will
|
||||||
|
stick, in pixels. (**Default value:** 0)
|
||||||
|
- `zIndex` controls where in the stacking context the header will end
|
||||||
|
up. Useful to prevent elements in rows from appearing over the header.
|
||||||
|
(**Default value:** 0)
|
||||||
|
|
||||||
|
Headers are never sticky on mobile-sized viewports because doing so causes some
|
||||||
|
accessibility issues with zooming and panning.
|
||||||
|
|
||||||
|
-}
|
||||||
|
type alias StickyConfig =
|
||||||
|
{ topOffset : Float
|
||||||
|
, zIndex : Int
|
||||||
|
, pageBackgroundColor : Css.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defaultStickyConfig : StickyConfig
|
||||||
|
defaultStickyConfig =
|
||||||
|
{ topOffset = 0
|
||||||
|
, zIndex = 0
|
||||||
|
, pageBackgroundColor = Nri.Ui.Colors.V1.white
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stickyConfigStyles : StickyConfig -> List Style
|
||||||
|
stickyConfigStyles { topOffset, zIndex, pageBackgroundColor } =
|
||||||
|
[ Css.Global.children
|
||||||
|
[ Css.Global.thead
|
||||||
|
[ Css.position Css.sticky
|
||||||
|
, Css.top (Css.px topOffset)
|
||||||
|
, Css.zIndex (Css.int zIndex)
|
||||||
|
, Css.backgroundColor pageBackgroundColor
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
{-| Customize how the table is rendered, for example by adding sorting or
|
||||||
|
stickiness.
|
||||||
|
-}
|
||||||
|
type Attribute id msg
|
||||||
|
= Attribute (Config id msg -> Config id msg)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Sort a column. You can get an initial state with `init` or `initDescending`.
|
||||||
|
If you make this sorting interactive, you should store the state in your model
|
||||||
|
and provide it to this function instead of recreating it on every update.
|
||||||
|
-}
|
||||||
|
state : State id -> Attribute id msg
|
||||||
|
state state_ =
|
||||||
|
Attribute (\config -> { config | state = Just state_ })
|
||||||
|
|
||||||
|
|
||||||
|
{-| Add interactivity in sorting columns. When this attribute is provided and
|
||||||
|
sorting is enabled, columns will be sortable by clicking the headers.
|
||||||
|
-}
|
||||||
|
updateMsg : (State id -> msg) -> Attribute id msg
|
||||||
|
updateMsg updateMsg_ =
|
||||||
|
Attribute (\config -> { config | updateMsg = Just updateMsg_ })
|
||||||
|
|
||||||
|
|
||||||
|
{-| Make the header sticky (that is, it will stick to the top of the viewport
|
||||||
|
when it otherwise would have been scrolled off.) You probably will want to set a
|
||||||
|
background color on the header as well.
|
||||||
|
-}
|
||||||
|
stickyHeader : Attribute id msg
|
||||||
|
stickyHeader =
|
||||||
|
Attribute (\config -> { config | stickyHeader = Just defaultStickyConfig })
|
||||||
|
|
||||||
|
|
||||||
|
{-| Does the same thing as `stickyHeader`, but with adaptations for your
|
||||||
|
specific use.
|
||||||
|
-}
|
||||||
|
stickyHeaderCustom : StickyConfig -> Attribute id msg
|
||||||
|
stickyHeaderCustom stickyConfig =
|
||||||
|
Attribute (\config -> { config | stickyHeader = Just stickyConfig })
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
init : id -> State id
|
||||||
|
init initialSort =
|
||||||
|
{ column = initialSort
|
||||||
|
, sortDirection = Ascending
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
initDescending : id -> State id
|
||||||
|
initDescending initialSort =
|
||||||
|
{ column = initialSort
|
||||||
|
, sortDirection = Descending
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
string :
|
||||||
|
{ id : id
|
||||||
|
, header : String
|
||||||
|
, value : entry -> String
|
||||||
|
, width : Int
|
||||||
|
, cellStyles : entry -> List Style
|
||||||
|
}
|
||||||
|
-> Column id entry msg
|
||||||
|
string { id, header, value, width, cellStyles } =
|
||||||
|
Column
|
||||||
|
{ id = id
|
||||||
|
, header = Html.text header
|
||||||
|
, view = value >> Html.text
|
||||||
|
, sorter = Just (simpleSort value)
|
||||||
|
, width = width
|
||||||
|
, cellStyles = cellStyles
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
custom :
|
||||||
|
{ id : id
|
||||||
|
, header : Html msg
|
||||||
|
, view : entry -> Html msg
|
||||||
|
, sorter : Maybe (Sorter entry)
|
||||||
|
, width : Int
|
||||||
|
, cellStyles : entry -> List Style
|
||||||
|
}
|
||||||
|
-> Column id entry msg
|
||||||
|
custom config =
|
||||||
|
Column
|
||||||
|
{ id = config.id
|
||||||
|
, header = config.header
|
||||||
|
, view = config.view
|
||||||
|
, sorter = config.sorter
|
||||||
|
, width = config.width
|
||||||
|
, cellStyles = config.cellStyles
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{-| Create a sorter function that always orders the entries in the same order.
|
||||||
|
For example, this is useful when we want to resolve ties and sort the tied
|
||||||
|
entries by name, no matter of the sort direction set on the table.
|
||||||
|
-}
|
||||||
|
invariantSort : (entry -> comparable) -> Sorter entry
|
||||||
|
invariantSort mapper =
|
||||||
|
\_ elem1 elem2 ->
|
||||||
|
compare (mapper elem1) (mapper elem2)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Create a simple sorter function that orders entries by mapping a function
|
||||||
|
over the collection. It will also reverse it when the sort direction is descending.
|
||||||
|
-}
|
||||||
|
simpleSort : (entry -> comparable) -> Sorter entry
|
||||||
|
simpleSort mapper =
|
||||||
|
\sortDirection elem1 elem2 ->
|
||||||
|
let
|
||||||
|
result =
|
||||||
|
compare (mapper elem1) (mapper elem2)
|
||||||
|
in
|
||||||
|
case sortDirection of
|
||||||
|
Ascending ->
|
||||||
|
result
|
||||||
|
|
||||||
|
Descending ->
|
||||||
|
flipOrder result
|
||||||
|
|
||||||
|
|
||||||
|
flipOrder : Order -> Order
|
||||||
|
flipOrder order =
|
||||||
|
case order of
|
||||||
|
LT ->
|
||||||
|
GT
|
||||||
|
|
||||||
|
EQ ->
|
||||||
|
EQ
|
||||||
|
|
||||||
|
GT ->
|
||||||
|
LT
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
combineSorters : List (Sorter entry) -> Sorter entry
|
||||||
|
combineSorters sorters =
|
||||||
|
\sortDirection elem1 elem2 ->
|
||||||
|
let
|
||||||
|
folder =
|
||||||
|
\sorter acc ->
|
||||||
|
case acc of
|
||||||
|
EQ ->
|
||||||
|
sorter sortDirection elem1 elem2
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
acc
|
||||||
|
in
|
||||||
|
List.foldl folder EQ sorters
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
view : List (Attribute id msg) -> List (Column id entry msg) -> List entry -> Html msg
|
||||||
|
view attributes columns entries =
|
||||||
|
let
|
||||||
|
config =
|
||||||
|
List.foldl (\(Attribute fn) soFar -> fn soFar) defaultConfig attributes
|
||||||
|
|
||||||
|
stickyStyles =
|
||||||
|
Maybe.map stickyConfigStyles config.stickyHeader
|
||||||
|
|> Maybe.withDefault []
|
||||||
|
|
||||||
|
tableColumns =
|
||||||
|
List.map (buildTableColumn config.updateMsg config.state) columns
|
||||||
|
in
|
||||||
|
case config.state of
|
||||||
|
Just state_ ->
|
||||||
|
let
|
||||||
|
sorter =
|
||||||
|
findSorter columns state_.column
|
||||||
|
in
|
||||||
|
Table.view stickyStyles
|
||||||
|
tableColumns
|
||||||
|
(List.sortWith (sorter state_.sortDirection) entries)
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
Table.view stickyStyles tableColumns entries
|
||||||
|
|
||||||
|
|
||||||
|
{-| -}
|
||||||
|
viewLoading : List (Attribute id msg) -> List (Column id entry msg) -> Html msg
|
||||||
|
viewLoading attributes columns =
|
||||||
|
let
|
||||||
|
config =
|
||||||
|
List.foldl (\(Attribute fn) soFar -> fn soFar) defaultConfig attributes
|
||||||
|
|
||||||
|
stickyStyles =
|
||||||
|
Maybe.map stickyConfigStyles config.stickyHeader
|
||||||
|
|> Maybe.withDefault []
|
||||||
|
|
||||||
|
tableColumns =
|
||||||
|
List.map (buildTableColumn config.updateMsg config.state) columns
|
||||||
|
in
|
||||||
|
Table.viewLoading stickyStyles tableColumns
|
||||||
|
|
||||||
|
|
||||||
|
findSorter : List (Column id entry msg) -> id -> Sorter entry
|
||||||
|
findSorter columns columnId =
|
||||||
|
columns
|
||||||
|
|> listExtraFind (\(Column column) -> column.id == columnId)
|
||||||
|
|> Maybe.andThen (\(Column column) -> column.sorter)
|
||||||
|
|> Maybe.withDefault identitySorter
|
||||||
|
|
||||||
|
|
||||||
|
{-| Taken from <https://github.com/elm-community/list-extra/blob/8.2.0/src/List/Extra.elm#L556>
|
||||||
|
-}
|
||||||
|
listExtraFind : (a -> Bool) -> List a -> Maybe a
|
||||||
|
listExtraFind predicate list =
|
||||||
|
case list of
|
||||||
|
[] ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
first :: rest ->
|
||||||
|
if predicate first then
|
||||||
|
Just first
|
||||||
|
|
||||||
|
else
|
||||||
|
listExtraFind predicate rest
|
||||||
|
|
||||||
|
|
||||||
|
identitySorter : Sorter a
|
||||||
|
identitySorter =
|
||||||
|
\_ _ _ ->
|
||||||
|
EQ
|
||||||
|
|
||||||
|
|
||||||
|
buildTableColumn : Maybe (State id -> msg) -> Maybe (State id) -> Column id entry msg -> Table.Column entry msg
|
||||||
|
buildTableColumn maybeUpdateMsg maybeState (Column column) =
|
||||||
|
Table.custom
|
||||||
|
{ header =
|
||||||
|
case maybeState of
|
||||||
|
Just state_ ->
|
||||||
|
viewSortHeader (column.sorter /= Nothing) column.header maybeUpdateMsg state_ column.id
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
column.header
|
||||||
|
, view = column.view
|
||||||
|
, width = Css.px (toFloat column.width)
|
||||||
|
, cellStyles = column.cellStyles
|
||||||
|
, sort =
|
||||||
|
Maybe.andThen
|
||||||
|
(\state_ ->
|
||||||
|
if state_.column == column.id then
|
||||||
|
Just state_.sortDirection
|
||||||
|
|
||||||
|
else
|
||||||
|
Nothing
|
||||||
|
)
|
||||||
|
maybeState
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewSortHeader : Bool -> Html msg -> Maybe (State id -> msg) -> State id -> id -> Html msg
|
||||||
|
viewSortHeader isSortable header maybeUpdateMsg state_ id =
|
||||||
|
let
|
||||||
|
nextState =
|
||||||
|
nextTableState state_ id
|
||||||
|
in
|
||||||
|
if isSortable then
|
||||||
|
Html.button
|
||||||
|
[ css
|
||||||
|
[ Css.displayFlex
|
||||||
|
, Css.alignItems Css.center
|
||||||
|
, Css.justifyContent Css.spaceBetween
|
||||||
|
, CssVendorPrefix.property "user-select" "none"
|
||||||
|
, if state_.column == id then
|
||||||
|
fontWeight bold
|
||||||
|
|
||||||
|
else
|
||||||
|
fontWeight normal
|
||||||
|
, cursor pointer
|
||||||
|
|
||||||
|
-- make this look less "buttony"
|
||||||
|
, Css.border Css.zero
|
||||||
|
, Css.backgroundColor Css.transparent
|
||||||
|
, Css.width (Css.pct 100)
|
||||||
|
, Css.height (Css.pct 100)
|
||||||
|
, Css.margin Css.zero
|
||||||
|
, Css.padding Css.zero
|
||||||
|
, Fonts.baseFont
|
||||||
|
, Css.fontSize (Css.em 1)
|
||||||
|
]
|
||||||
|
, maybe (\updateMsg_ -> Html.Styled.Events.onClick (updateMsg_ nextState)) maybeUpdateMsg
|
||||||
|
|
||||||
|
-- screen readers should know what clicking this button will do
|
||||||
|
, Aria.roleDescription "sort button"
|
||||||
|
]
|
||||||
|
[ Html.div [] [ header ]
|
||||||
|
, viewJust (\_ -> viewSortButton state_ id) maybeUpdateMsg
|
||||||
|
]
|
||||||
|
|
||||||
|
else
|
||||||
|
Html.div
|
||||||
|
[ css [ fontWeight normal ]
|
||||||
|
]
|
||||||
|
[ header ]
|
||||||
|
|
||||||
|
|
||||||
|
viewSortButton : State id -> id -> Html msg
|
||||||
|
viewSortButton state_ id =
|
||||||
|
let
|
||||||
|
arrows upHighlighted downHighlighted =
|
||||||
|
Html.div
|
||||||
|
[ css
|
||||||
|
[ Css.displayFlex
|
||||||
|
, Css.flexDirection Css.column
|
||||||
|
, Css.alignItems Css.center
|
||||||
|
, Css.justifyContent Css.center
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ sortArrow Up upHighlighted
|
||||||
|
, sortArrow Down downHighlighted
|
||||||
|
]
|
||||||
|
|
||||||
|
buttonContent =
|
||||||
|
case ( state_.column == id, state_.sortDirection ) of
|
||||||
|
( True, Ascending ) ->
|
||||||
|
arrows True False
|
||||||
|
|
||||||
|
( True, Descending ) ->
|
||||||
|
arrows False True
|
||||||
|
|
||||||
|
( False, _ ) ->
|
||||||
|
arrows False False
|
||||||
|
in
|
||||||
|
Html.div [ css [ padding (px 2) ] ] [ buttonContent ]
|
||||||
|
|
||||||
|
|
||||||
|
nextTableState : State id -> id -> State id
|
||||||
|
nextTableState state_ id =
|
||||||
|
if state_.column == id then
|
||||||
|
{ column = id
|
||||||
|
, sortDirection = flipSortDirection state_.sortDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{ column = id
|
||||||
|
, sortDirection = Ascending
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
flipSortDirection : SortDirection -> SortDirection
|
||||||
|
flipSortDirection order =
|
||||||
|
case order of
|
||||||
|
Ascending ->
|
||||||
|
Descending
|
||||||
|
|
||||||
|
Descending ->
|
||||||
|
Ascending
|
||||||
|
|
||||||
|
|
||||||
|
type Direction
|
||||||
|
= Up
|
||||||
|
| Down
|
||||||
|
|
||||||
|
|
||||||
|
sortArrow : Direction -> Bool -> Html msg
|
||||||
|
sortArrow direction active =
|
||||||
|
let
|
||||||
|
arrow =
|
||||||
|
case direction of
|
||||||
|
Up ->
|
||||||
|
Nri.Ui.UiIcon.V1.sortArrow
|
||||||
|
|
||||||
|
Down ->
|
||||||
|
Nri.Ui.UiIcon.V1.sortArrowDown
|
||||||
|
|
||||||
|
color =
|
||||||
|
if active then
|
||||||
|
Nri.Ui.Colors.V1.azure
|
||||||
|
|
||||||
|
else
|
||||||
|
Nri.Ui.Colors.V1.gray75
|
||||||
|
in
|
||||||
|
arrow
|
||||||
|
|> Nri.Ui.Svg.V1.withHeight (px 6)
|
||||||
|
|> Nri.Ui.Svg.V1.withWidth (px 8)
|
||||||
|
|> Nri.Ui.Svg.V1.withColor color
|
||||||
|
|> Nri.Ui.Svg.V1.withCss
|
||||||
|
[ displayFlex
|
||||||
|
, margin2 (px 1) zero
|
||||||
|
]
|
||||||
|
|> Nri.Ui.Svg.V1.toHtml
|
336
src/Nri/Ui/Table/V7.elm
Normal file
336
src/Nri/Ui/Table/V7.elm
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
module Nri.Ui.Table.V7 exposing
|
||||||
|
( Column, SortDirection(..), custom, string, rowHeader
|
||||||
|
, view, viewWithoutHeader
|
||||||
|
, viewLoading, viewLoadingWithoutHeader
|
||||||
|
)
|
||||||
|
|
||||||
|
{-| Upgrading from V6:
|
||||||
|
|
||||||
|
- If you don't have extra styles for the table, add an empty list to calls
|
||||||
|
to `view`.
|
||||||
|
- Consider moving to `SortableTable`, as in V4 a non-interactive table renders
|
||||||
|
just the same but has additional flexibility in the API and will make future
|
||||||
|
upgrades easier.
|
||||||
|
|
||||||
|
@docs Column, SortDirection, custom, string, rowHeader
|
||||||
|
|
||||||
|
@docs view, viewWithoutHeader
|
||||||
|
|
||||||
|
@docs viewLoading, viewLoadingWithoutHeader
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Accessibility.Styled.Style as Style
|
||||||
|
import Css exposing (..)
|
||||||
|
import Css.Animations
|
||||||
|
import Html.Styled as Html exposing (..)
|
||||||
|
import Html.Styled.Attributes as Attributes exposing (css)
|
||||||
|
import Nri.Ui.Colors.V1 exposing (..)
|
||||||
|
import Nri.Ui.Fonts.V1 exposing (baseFont)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Closed representation of how to render the header and cells of a column
|
||||||
|
in the table
|
||||||
|
-}
|
||||||
|
type Column data msg
|
||||||
|
= Column (Html msg) (data -> Html msg) Style (data -> List Style) (Maybe SortDirection) CellType
|
||||||
|
|
||||||
|
|
||||||
|
{-| Which direction is a table column sorted? Only set these on columns that
|
||||||
|
actually have an explicit sort!
|
||||||
|
-}
|
||||||
|
type SortDirection
|
||||||
|
= Ascending
|
||||||
|
| Descending
|
||||||
|
|
||||||
|
|
||||||
|
{-| Is this cell a data cell or header cell?
|
||||||
|
-}
|
||||||
|
type CellType
|
||||||
|
= RowHeaderCell
|
||||||
|
| DataCell
|
||||||
|
|
||||||
|
|
||||||
|
cell : CellType -> List (Attribute msg) -> List (Html msg) -> Html msg
|
||||||
|
cell cellType attrs =
|
||||||
|
case cellType of
|
||||||
|
RowHeaderCell ->
|
||||||
|
th (Attributes.scope "row" :: attrs)
|
||||||
|
|
||||||
|
DataCell ->
|
||||||
|
td attrs
|
||||||
|
|
||||||
|
|
||||||
|
{-| A column that renders some aspect of a value as text
|
||||||
|
-}
|
||||||
|
string :
|
||||||
|
{ header : String
|
||||||
|
, value : data -> String
|
||||||
|
, width : LengthOrAuto compatible
|
||||||
|
, cellStyles : data -> List Style
|
||||||
|
, sort : Maybe SortDirection
|
||||||
|
}
|
||||||
|
-> Column data msg
|
||||||
|
string { header, value, width, cellStyles, sort } =
|
||||||
|
Column (Html.text header) (value >> Html.text) (Css.width width) cellStyles sort DataCell
|
||||||
|
|
||||||
|
|
||||||
|
{-| A column that renders however you want it to
|
||||||
|
-}
|
||||||
|
custom :
|
||||||
|
{ header : Html msg
|
||||||
|
, view : data -> Html msg
|
||||||
|
, width : LengthOrAuto compatible
|
||||||
|
, cellStyles : data -> List Style
|
||||||
|
, sort : Maybe SortDirection
|
||||||
|
}
|
||||||
|
-> Column data msg
|
||||||
|
custom options =
|
||||||
|
Column options.header options.view (Css.width options.width) options.cellStyles options.sort DataCell
|
||||||
|
|
||||||
|
|
||||||
|
{-| A column whose cells are row headers
|
||||||
|
-}
|
||||||
|
rowHeader :
|
||||||
|
{ header : Html msg
|
||||||
|
, view : data -> Html msg
|
||||||
|
, width : LengthOrAuto compatible
|
||||||
|
, cellStyles : data -> List Style
|
||||||
|
, sort : Maybe SortDirection
|
||||||
|
}
|
||||||
|
-> Column data msg
|
||||||
|
rowHeader options =
|
||||||
|
Column options.header options.view (Css.width options.width) options.cellStyles options.sort RowHeaderCell
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
{-| Displays a table of data without a header row
|
||||||
|
-}
|
||||||
|
viewWithoutHeader : List Style -> List (Column data msg) -> List data -> Html msg
|
||||||
|
viewWithoutHeader additionalStyles columns =
|
||||||
|
tableWithoutHeader additionalStyles columns (viewRow columns)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Displays a table of data based on the provided column definitions
|
||||||
|
-}
|
||||||
|
view : List Style -> List (Column data msg) -> List data -> Html msg
|
||||||
|
view additionalStyles columns =
|
||||||
|
tableWithHeader additionalStyles columns (viewRow columns)
|
||||||
|
|
||||||
|
|
||||||
|
viewRow : List (Column data msg) -> data -> Html msg
|
||||||
|
viewRow columns data =
|
||||||
|
tr
|
||||||
|
[ css rowStyles ]
|
||||||
|
(List.map (viewColumn data) columns)
|
||||||
|
|
||||||
|
|
||||||
|
viewColumn : data -> Column data msg -> Html msg
|
||||||
|
viewColumn data (Column _ renderer width cellStyles _ cellType) =
|
||||||
|
cell cellType
|
||||||
|
[ css ([ width, verticalAlign middle ] ++ cellStyles data)
|
||||||
|
]
|
||||||
|
[ renderer data ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW LOADING
|
||||||
|
|
||||||
|
|
||||||
|
{-| Display a table with the given columns but instead of data, show blocked
|
||||||
|
out text with an interesting animation. This view lets the user know that
|
||||||
|
data is on its way and what it will look like when it arrives.
|
||||||
|
-}
|
||||||
|
viewLoading : List Style -> List (Column data msg) -> Html msg
|
||||||
|
viewLoading additionalStyles columns =
|
||||||
|
tableWithHeader (loadingTableStyles ++ additionalStyles) columns (viewLoadingRow columns) (List.range 0 8)
|
||||||
|
|
||||||
|
|
||||||
|
{-| Display the loading table without a header row
|
||||||
|
-}
|
||||||
|
viewLoadingWithoutHeader : List Style -> List (Column data msg) -> Html msg
|
||||||
|
viewLoadingWithoutHeader additionalStyles columns =
|
||||||
|
tableWithoutHeader (loadingTableStyles ++ additionalStyles) columns (viewLoadingRow columns) (List.range 0 8)
|
||||||
|
|
||||||
|
|
||||||
|
viewLoadingRow : List (Column data msg) -> Int -> Html msg
|
||||||
|
viewLoadingRow columns index =
|
||||||
|
tr
|
||||||
|
[ css rowStyles ]
|
||||||
|
(List.indexedMap (viewLoadingColumn index) columns)
|
||||||
|
|
||||||
|
|
||||||
|
viewLoadingColumn : Int -> Int -> Column data msg -> Html msg
|
||||||
|
viewLoadingColumn rowIndex colIndex (Column _ _ width _ _ cellType) =
|
||||||
|
cell cellType
|
||||||
|
[ css (stylesLoadingColumn rowIndex colIndex width ++ [ verticalAlign middle ] ++ loadingCellStyles)
|
||||||
|
]
|
||||||
|
[ span [ css loadingContentStyles ] [] ]
|
||||||
|
|
||||||
|
|
||||||
|
stylesLoadingColumn : Int -> Int -> Style -> List Style
|
||||||
|
stylesLoadingColumn rowIndex colIndex width =
|
||||||
|
[ width
|
||||||
|
, property "animation-delay" (String.fromFloat (toFloat (rowIndex + colIndex) * 0.1) ++ "s")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- HELP
|
||||||
|
|
||||||
|
|
||||||
|
tableWithoutHeader : List Style -> List (Column data msg) -> (a -> Html msg) -> List a -> Html msg
|
||||||
|
tableWithoutHeader styles columns toRow data =
|
||||||
|
table styles
|
||||||
|
[ thead [] [ tr Style.invisible (List.map tableColHeader columns) ]
|
||||||
|
, tableBody toRow data
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
tableWithHeader : List Style -> List (Column data msg) -> (a -> Html msg) -> List a -> Html msg
|
||||||
|
tableWithHeader styles columns toRow data =
|
||||||
|
table styles
|
||||||
|
[ tableHeader columns
|
||||||
|
, tableBody toRow data
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
table : List Style -> List (Html msg) -> Html msg
|
||||||
|
table styles =
|
||||||
|
Html.table [ css (styles ++ tableStyles) ]
|
||||||
|
|
||||||
|
|
||||||
|
tableHeader : List (Column data msg) -> Html msg
|
||||||
|
tableHeader columns =
|
||||||
|
thead []
|
||||||
|
[ tr [ css headersStyles ]
|
||||||
|
(List.map tableColHeader columns)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
tableColHeader : Column data msg -> Html msg
|
||||||
|
tableColHeader (Column header _ width _ sort _) =
|
||||||
|
th
|
||||||
|
[ Attributes.scope "col"
|
||||||
|
, css (width :: headerStyles)
|
||||||
|
, Attributes.attribute "aria-sort" <|
|
||||||
|
case sort of
|
||||||
|
Nothing ->
|
||||||
|
"none"
|
||||||
|
|
||||||
|
Just Ascending ->
|
||||||
|
"ascending"
|
||||||
|
|
||||||
|
Just Descending ->
|
||||||
|
"descending"
|
||||||
|
]
|
||||||
|
[ header ]
|
||||||
|
|
||||||
|
|
||||||
|
tableBody : (a -> Html msg) -> List a -> Html msg
|
||||||
|
tableBody toRow items =
|
||||||
|
tbody [] (List.map toRow items)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- STYLES
|
||||||
|
|
||||||
|
|
||||||
|
headersStyles : List Style
|
||||||
|
headersStyles =
|
||||||
|
[ -- We use a inset box shadown for a bottom border instead of an actual
|
||||||
|
-- border because with our use of `border-collapse: collapse`, the bottom
|
||||||
|
-- gray border sticks to the table instead of traveling with the header
|
||||||
|
-- when the header has `position: sticky` applied.
|
||||||
|
boxShadow4 inset (px 0) (px -3) gray75
|
||||||
|
, height (px 45)
|
||||||
|
, fontSize (px 15)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
headerStyles : List Style
|
||||||
|
headerStyles =
|
||||||
|
[ padding4 (px 11) (px 12) (px 14) (px 12)
|
||||||
|
, textAlign left
|
||||||
|
, fontWeight bold
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
rowStyles : List Style
|
||||||
|
rowStyles =
|
||||||
|
[ height (px 45)
|
||||||
|
, fontSize (px 14)
|
||||||
|
, color gray20
|
||||||
|
, pseudoClass "nth-child(odd)"
|
||||||
|
[ backgroundColor gray96 ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
loadingContentStyles : List Style
|
||||||
|
loadingContentStyles =
|
||||||
|
[ width (pct 100)
|
||||||
|
, display inlineBlock
|
||||||
|
, height (Css.em 1)
|
||||||
|
, borderRadius (Css.em 1)
|
||||||
|
, backgroundColor gray75
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
loadingCellStyles : List Style
|
||||||
|
loadingCellStyles =
|
||||||
|
[ batch flashAnimation
|
||||||
|
, padding2 (px 14) (px 10)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
loadingTableStyles : List Style
|
||||||
|
loadingTableStyles =
|
||||||
|
fadeInAnimation
|
||||||
|
|
||||||
|
|
||||||
|
tableStyles : List Style
|
||||||
|
tableStyles =
|
||||||
|
[ borderCollapse collapse
|
||||||
|
, baseFont
|
||||||
|
, Css.width (Css.pct 100)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
flash : Css.Animations.Keyframes {}
|
||||||
|
flash =
|
||||||
|
Css.Animations.keyframes
|
||||||
|
[ ( 0, [ Css.Animations.opacity (Css.num 0.6) ] )
|
||||||
|
, ( 50, [ Css.Animations.opacity (Css.num 0.2) ] )
|
||||||
|
, ( 100, [ Css.Animations.opacity (Css.num 0.6) ] )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
fadeIn : Css.Animations.Keyframes {}
|
||||||
|
fadeIn =
|
||||||
|
Css.Animations.keyframes
|
||||||
|
[ ( 0, [ Css.Animations.opacity (Css.num 0) ] )
|
||||||
|
, ( 100, [ Css.Animations.opacity (Css.num 1) ] )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
flashAnimation : List Css.Style
|
||||||
|
flashAnimation =
|
||||||
|
[ animationName flash
|
||||||
|
, property "animation-duration" "2s"
|
||||||
|
, property "animation-iteration-count" "infinite"
|
||||||
|
, opacity (num 0.6)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
fadeInAnimation : List Css.Style
|
||||||
|
fadeInAnimation =
|
||||||
|
[ animationName fadeIn
|
||||||
|
, property "animation-duration" "0.4s"
|
||||||
|
, property "animation-delay" "0.2s"
|
||||||
|
, property "animation-fill-mode" "forwards"
|
||||||
|
, animationIterationCount (int 1)
|
||||||
|
, opacity (num 0)
|
||||||
|
]
|
@ -2,7 +2,8 @@ module Spec.Nri.Ui.SortableTable exposing (spec)
|
|||||||
|
|
||||||
import Expect
|
import Expect
|
||||||
import Html.Styled
|
import Html.Styled
|
||||||
import Nri.Ui.SortableTable.V3 as SortableTable
|
import Nri.Ui.SortableTable.V4 as SortableTable
|
||||||
|
import Nri.Ui.Table.V7 exposing (SortDirection)
|
||||||
import Test exposing (..)
|
import Test exposing (..)
|
||||||
import Test.Html.Query as Query
|
import Test.Html.Query as Query
|
||||||
import Test.Html.Selector as Selector
|
import Test.Html.Selector as Selector
|
||||||
@ -19,30 +20,23 @@ type alias Person =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
columns : List (SortableTable.Column Column Person msg)
|
||||||
= NoOp
|
columns =
|
||||||
|
[ SortableTable.string
|
||||||
|
{ id = FirstName
|
||||||
config : SortableTable.Config Column Person Msg
|
, header = "First name"
|
||||||
config =
|
, value = .firstName
|
||||||
{ updateMsg = \_ -> NoOp
|
, width = 125
|
||||||
, columns =
|
, cellStyles = \_ -> []
|
||||||
[ SortableTable.string
|
}
|
||||||
{ id = FirstName
|
, SortableTable.string
|
||||||
, header = "First name"
|
{ id = LastName
|
||||||
, value = .firstName
|
, header = "Last name"
|
||||||
, width = 125
|
, value = .lastName
|
||||||
, cellStyles = \_ -> []
|
, width = 125
|
||||||
}
|
, cellStyles = \_ -> []
|
||||||
, SortableTable.string
|
}
|
||||||
{ id = LastName
|
]
|
||||||
, header = "Last name"
|
|
||||||
, value = .lastName
|
|
||||||
, width = 125
|
|
||||||
, cellStyles = \_ -> []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
entries : List Person
|
entries : List Person
|
||||||
@ -53,9 +47,9 @@ entries =
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
tableView : SortableTable.State Column -> Query.Single Msg
|
tableView : SortableTable.State Column -> Query.Single msg
|
||||||
tableView sortState =
|
tableView sortState =
|
||||||
SortableTable.view config sortState entries
|
SortableTable.view [ SortableTable.state sortState ] columns entries
|
||||||
|> Html.Styled.toUnstyled
|
|> Html.Styled.toUnstyled
|
||||||
|> Query.fromHtml
|
|> Query.fromHtml
|
||||||
|
|
||||||
@ -70,10 +64,12 @@ sortByDescending field =
|
|||||||
SortableTable.initDescending field
|
SortableTable.initDescending field
|
||||||
|
|
||||||
|
|
||||||
|
ascending : SortDirection
|
||||||
ascending =
|
ascending =
|
||||||
sortBy FirstName |> .sortDirection
|
sortBy FirstName |> .sortDirection
|
||||||
|
|
||||||
|
|
||||||
|
descending : SortDirection
|
||||||
descending =
|
descending =
|
||||||
sortByDescending FirstName |> .sortDirection
|
sortByDescending FirstName |> .sortDirection
|
||||||
|
|
||||||
|
@ -68,11 +68,13 @@
|
|||||||
"Nri.Ui.Shadows.V1",
|
"Nri.Ui.Shadows.V1",
|
||||||
"Nri.Ui.SideNav.V4",
|
"Nri.Ui.SideNav.V4",
|
||||||
"Nri.Ui.SortableTable.V3",
|
"Nri.Ui.SortableTable.V3",
|
||||||
|
"Nri.Ui.SortableTable.V4",
|
||||||
"Nri.Ui.Spacing.V1",
|
"Nri.Ui.Spacing.V1",
|
||||||
"Nri.Ui.Sprite.V1",
|
"Nri.Ui.Sprite.V1",
|
||||||
"Nri.Ui.Svg.V1",
|
"Nri.Ui.Svg.V1",
|
||||||
"Nri.Ui.Switch.V2",
|
"Nri.Ui.Switch.V2",
|
||||||
"Nri.Ui.Table.V6",
|
"Nri.Ui.Table.V6",
|
||||||
|
"Nri.Ui.Table.V7",
|
||||||
"Nri.Ui.Tabs.V6",
|
"Nri.Ui.Tabs.V6",
|
||||||
"Nri.Ui.Tabs.V7",
|
"Nri.Ui.Tabs.V7",
|
||||||
"Nri.Ui.Tabs.V8",
|
"Nri.Ui.Tabs.V8",
|
||||||
|
Loading…
Reference in New Issue
Block a user