Initial commit with draft API
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					elm-stuff
 | 
				
			||||||
							
								
								
									
										30
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					Copyright (c) 2016-present, Evan Czaplicki
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					modification, are permitted provided that the following conditions are met:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					      notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    * Redistributions in binary form must reproduce the above
 | 
				
			||||||
 | 
					      copyright notice, this list of conditions and the following
 | 
				
			||||||
 | 
					      disclaimer in the documentation and/or other materials provided
 | 
				
			||||||
 | 
					      with the distribution.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    * Neither the name of Evan Czaplicki nor the names of other
 | 
				
			||||||
 | 
					      contributors may be used to endorse or promote products derived
 | 
				
			||||||
 | 
					      from this software without specific prior written permission.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
							
								
								
									
										17
									
								
								elm-package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								elm-package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "version": "1.0.0",
 | 
				
			||||||
 | 
					    "summary": "Sortable tables for data of any shape.",
 | 
				
			||||||
 | 
					    "repository": "https://github.com/evancz/elm-table.git",
 | 
				
			||||||
 | 
					    "license": "BSD3",
 | 
				
			||||||
 | 
					    "source-directories": [
 | 
				
			||||||
 | 
					        "src"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "exposed-modules": [
 | 
				
			||||||
 | 
					        "Table"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "dependencies": {
 | 
				
			||||||
 | 
					        "elm-lang/core": "4.0.0 <= v < 5.0.0",
 | 
				
			||||||
 | 
					        "elm-lang/html": "1.1.0 <= v < 2.0.0"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "elm-version": "0.17.0 <= v < 0.18.0"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										131
									
								
								examples/Presidents.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								examples/Presidents.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					import Html exposing (Html, div, h1, text)
 | 
				
			||||||
 | 
					import Html.App as App
 | 
				
			||||||
 | 
					import Table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main =
 | 
				
			||||||
 | 
					  App.program
 | 
				
			||||||
 | 
					    { init = init presidents
 | 
				
			||||||
 | 
					    , update = update
 | 
				
			||||||
 | 
					    , view = view
 | 
				
			||||||
 | 
					    , subscriptions = \_ -> Sub.none
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- MODEL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type alias Model =
 | 
				
			||||||
 | 
					  { people : List Person
 | 
				
			||||||
 | 
					  , tableState : Table.State
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					init : List Person -> ( Model, Cmd Msg )
 | 
				
			||||||
 | 
					init people =
 | 
				
			||||||
 | 
					  ( Model people (Table.ascending "Year")
 | 
				
			||||||
 | 
					  , Cmd.none
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- UPDATE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Msg
 | 
				
			||||||
 | 
					  = UpdateTableState Table.State
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					update : Msg -> Model -> ( Model, Cmd Msg )
 | 
				
			||||||
 | 
					update msg model =
 | 
				
			||||||
 | 
					  case msg of
 | 
				
			||||||
 | 
					    UpdateTableState newState ->
 | 
				
			||||||
 | 
					      ( Model model.people newState, Cmd.none )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- VIEW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					view : Model -> Html Msg
 | 
				
			||||||
 | 
					view {people, tableState} =
 | 
				
			||||||
 | 
					  div []
 | 
				
			||||||
 | 
					    [ h1 [] [ text "Birthplaces of U.S. Presidents" ]
 | 
				
			||||||
 | 
					    , Table.view config tableState people
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config : Table.Config Person Msg
 | 
				
			||||||
 | 
					config =
 | 
				
			||||||
 | 
					  Table.config
 | 
				
			||||||
 | 
					    { toId = .name
 | 
				
			||||||
 | 
					    , toMsg = UpdateTableState
 | 
				
			||||||
 | 
					    , columns =
 | 
				
			||||||
 | 
					        [ Table.stringColumn "Name" .name
 | 
				
			||||||
 | 
					        , Table.intColumn "Year" .year
 | 
				
			||||||
 | 
					        , Table.stringColumn "City" .city
 | 
				
			||||||
 | 
					        , Table.stringColumn "State" .state
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- PEOPLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type alias Person =
 | 
				
			||||||
 | 
					  { name : String
 | 
				
			||||||
 | 
					  , year : Int
 | 
				
			||||||
 | 
					  , city : String
 | 
				
			||||||
 | 
					  , state : String
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					presidents : List Person
 | 
				
			||||||
 | 
					presidents =
 | 
				
			||||||
 | 
					  [ Person "George Washington" 1732 "Westmoreland County" "Virginia"
 | 
				
			||||||
 | 
					  , Person "John Adams" 1735 "Braintree" "Massachusetts"
 | 
				
			||||||
 | 
					  , Person "Thomas Jefferson" 1743 "Shadwell" "Virginia"
 | 
				
			||||||
 | 
					  , Person "James Madison" 1751 "Port Conway" "Virginia"
 | 
				
			||||||
 | 
					  , Person "James Monroe" 1758 "Monroe Hall" "Virginia"
 | 
				
			||||||
 | 
					  , Person "Andrew Jackson" 1767 "Waxhaws Region" "South/North Carolina"
 | 
				
			||||||
 | 
					  , Person "John Quincy Adams" 1767 "Braintree" "Massachusetts"
 | 
				
			||||||
 | 
					  , Person "William Henry Harrison" 1773 "Charles City County" "Virginia"
 | 
				
			||||||
 | 
					  , Person "Martin Van Buren" 1782 "Kinderhook" "New York"
 | 
				
			||||||
 | 
					  , Person "Zachary Taylor" 1784 "Barboursville" "Virginia"
 | 
				
			||||||
 | 
					  , Person "John Tyler" 1790 "Charles City County" "Virginia"
 | 
				
			||||||
 | 
					  , Person "James Buchanan" 1791 "Cove Gap" "Pennsylvania"
 | 
				
			||||||
 | 
					  , Person "James K. Polk" 1795 "Pineville" "North Carolina"
 | 
				
			||||||
 | 
					  , Person "Millard Fillmore" 1800 "Summerhill" "New York"
 | 
				
			||||||
 | 
					  , Person "Franklin Pierce" 1804 "Hillsborough" "New Hampshire"
 | 
				
			||||||
 | 
					  , Person "Andrew Johnson" 1808 "Raleigh" "North Carolina"
 | 
				
			||||||
 | 
					  , Person "Abraham Lincoln" 1809 "Sinking spring" "Kentucky"
 | 
				
			||||||
 | 
					  , Person "Ulysses S. Grant" 1822 "Point Pleasant" "Ohio"
 | 
				
			||||||
 | 
					  , Person "Rutherford B. Hayes" 1822 "Delaware" "Ohio"
 | 
				
			||||||
 | 
					  , Person "Chester A. Arthur" 1829 "Fairfield" "Vermont"
 | 
				
			||||||
 | 
					  , Person "James A. Garfield" 1831 "Moreland Hills" "Ohio"
 | 
				
			||||||
 | 
					  , Person "Benjamin Harrison" 1833 "North Bend" "Ohio"
 | 
				
			||||||
 | 
					  , Person "Grover Cleveland" 1837 "Caldwell" "New Jersey"
 | 
				
			||||||
 | 
					  , Person "William McKinley" 1843 "Niles" "Ohio"
 | 
				
			||||||
 | 
					  , Person "Woodrow Wilson" 1856 "Staunton" "Virginia"
 | 
				
			||||||
 | 
					  , Person "William Howard Taft" 1857 "Cincinnati" "Ohio"
 | 
				
			||||||
 | 
					  , Person "Theodore Roosevelt" 1858 "New York City" "New York"
 | 
				
			||||||
 | 
					  , Person "Warren G. Harding" 1865 "Blooming Grove" "Ohio"
 | 
				
			||||||
 | 
					  , Person "Calvin Coolidge" 1872 "Plymouth" "Vermont"
 | 
				
			||||||
 | 
					  , Person "Herbert Hoover" 1874 "West Branch" "Iowa"
 | 
				
			||||||
 | 
					  , Person "Franklin D. Roosevelt" 1882 "Hyde Park" "New York"
 | 
				
			||||||
 | 
					  , Person "Harry S. Truman" 1884 "Lamar" "Missouri"
 | 
				
			||||||
 | 
					  , Person "Dwight D. Eisenhower" 1890 "Denison" "Texas"
 | 
				
			||||||
 | 
					  , Person "Lyndon B. Johnson" 1908 "Stonewall" "Texas"
 | 
				
			||||||
 | 
					  , Person "Ronald Reagan" 1911 "Tampico" "Illinois"
 | 
				
			||||||
 | 
					  , Person "Richard M. Nixon" 1913 "Yorba Linda" "California"
 | 
				
			||||||
 | 
					  , Person "Gerald R. Ford" 1913 "Omaha" "Nebraska"
 | 
				
			||||||
 | 
					  , Person "John F. Kennedy" 1917 "Brookline" "Massachusetts"
 | 
				
			||||||
 | 
					  , Person "George H. W. Bush" 1924 "Milton" "Massachusetts"
 | 
				
			||||||
 | 
					  , Person "Jimmy Carter" 1924 "Plains" "Georgia"
 | 
				
			||||||
 | 
					  , Person "George W. Bush" 1946 "New Haven" "Connecticut"
 | 
				
			||||||
 | 
					  , Person "Bill Clinton" 1946 "Hope" "Arkansas"
 | 
				
			||||||
 | 
					  , Person "Barack Obama" 1961 "Honolulu" "Hawaii"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
							
								
								
									
										591
									
								
								src/Table.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										591
									
								
								src/Table.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,591 @@
 | 
				
			|||||||
 | 
					module Table exposing
 | 
				
			||||||
 | 
					  ( view
 | 
				
			||||||
 | 
					  , State, initialSort
 | 
				
			||||||
 | 
					  , Config, config
 | 
				
			||||||
 | 
					  , Column, stringColumn, intColumn, floatColumn, column
 | 
				
			||||||
 | 
					  , Sorter, unsortable, increasingBy, decreasingBy
 | 
				
			||||||
 | 
					  , increasingOrDecreasingBy, decreasingOrIncreasingBy
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This library helps you create sortable tables. The crucial feature is that it
 | 
				
			||||||
 | 
					lets you own your data separately and keep it in whatever format is best for
 | 
				
			||||||
 | 
					you. This way you are free to change your data without worrying about the table
 | 
				
			||||||
 | 
					“getting out of sync” with the data. Having a single source of
 | 
				
			||||||
 | 
					truth is pretty great!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I recommend checking out the [examples][] to get a feel for how it works.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[examples]: https://github.com/evancz/elm-tables/tree/master/examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# View
 | 
				
			||||||
 | 
					@docs view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Configuration
 | 
				
			||||||
 | 
					@docs config, stringColumn, intColumn, floatColumn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# State
 | 
				
			||||||
 | 
					@docs State, initialSort
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Crazy Customization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you are new to this library, you can probably stop reading here. After this
 | 
				
			||||||
 | 
					point are a bunch of ways to customize your table further. If it does not
 | 
				
			||||||
 | 
					provide what you need, you may just want to write a custom table yourself. It
 | 
				
			||||||
 | 
					is not that crazy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Custom Columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@docs Column, customColumn, veryCustomColumn,
 | 
				
			||||||
 | 
					  Sorter, unsortable, increasingBy, decreasingBy,
 | 
				
			||||||
 | 
					  increasingOrDecreasingBy, decreasingOrIncreasingBy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Custom Tables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@docs Config, customConfig,
 | 
				
			||||||
 | 
					  Customizations, HtmlDetails, Status, defaultCustomizations
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Html exposing (Html, Attribute)
 | 
				
			||||||
 | 
					import Html.Attributes as Attr
 | 
				
			||||||
 | 
					import Html.Events as E
 | 
				
			||||||
 | 
					import Html.Keyed as Keyed
 | 
				
			||||||
 | 
					import Html.Lazy exposing (lazy2, lazy3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- STATE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Tracks which column to sort by.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type State =
 | 
				
			||||||
 | 
					  State String Bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Create a table state. By providing a column name, you determine which
 | 
				
			||||||
 | 
					column should be used for sorting by default. So if you want your table of
 | 
				
			||||||
 | 
					yachts to be sorted by length by default, you might say:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import Table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Table.initialSort "Length"
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					initialSort : String -> State
 | 
				
			||||||
 | 
					initialSort header =
 | 
				
			||||||
 | 
					  State header False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Configuration for your table, describing your columns.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Note:** Your `Config` should *never* be held in your model.
 | 
				
			||||||
 | 
					It should only appear in `view` code.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type Config data msg =
 | 
				
			||||||
 | 
					  Config
 | 
				
			||||||
 | 
					    { toId : data -> String
 | 
				
			||||||
 | 
					    , toMsg : State -> msg
 | 
				
			||||||
 | 
					    , columns : List (Column data msg)
 | 
				
			||||||
 | 
					    , customizations : Customizations data msg
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Create the `Config` for your `view` function. Everything you need to
 | 
				
			||||||
 | 
					render your columns efficiently and handle selection of columns.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Say we have a `List Person` that we want to show as a table. The table should
 | 
				
			||||||
 | 
					have a column for name and age. We would create a `Config` like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import Table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    type Msg = NewTableState State | ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    config : Table.Config Person Msg
 | 
				
			||||||
 | 
					    config =
 | 
				
			||||||
 | 
					      Table.config
 | 
				
			||||||
 | 
					        { toId = .name
 | 
				
			||||||
 | 
					        , toMsg = NewTableState
 | 
				
			||||||
 | 
					        , columns =
 | 
				
			||||||
 | 
					            [ Table.stringColumn "Name" .name
 | 
				
			||||||
 | 
					            , Table.intColumn "Age" .age
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You provide the following information in your table configuration:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - `toId` — turn a `Person` into a unique ID. This lets us use
 | 
				
			||||||
 | 
					  [`Html.Keyed`][keyed] under the hood to make resorts faster.
 | 
				
			||||||
 | 
					  - `columns` — specify some columns to show.
 | 
				
			||||||
 | 
					  - `toMsg` — a way send new table states to your app as messages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See the [examples][] to get a better feel for this!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[keyed]: http://package.elm-lang.org/packages/elm-lang/html/latest/Html-Keyed
 | 
				
			||||||
 | 
					[examples]: https://github.com/evancz/elm-tables/tree/master/examples
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					config
 | 
				
			||||||
 | 
					  : { toId : data -> String
 | 
				
			||||||
 | 
					    , toMsg : State -> msg
 | 
				
			||||||
 | 
					    , columns : List (Column data msg)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  -> Config data msg
 | 
				
			||||||
 | 
					config { toId, toMsg, columns } =
 | 
				
			||||||
 | 
					  Config
 | 
				
			||||||
 | 
					    { toId = toId
 | 
				
			||||||
 | 
					    , toMsg = toMsg
 | 
				
			||||||
 | 
					    , columns = List.map (\(Column cData) -> cData) columns
 | 
				
			||||||
 | 
					    , customizations = defaultCustomizations
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					customConfig
 | 
				
			||||||
 | 
					  : { toId : data -> String
 | 
				
			||||||
 | 
					    , toMsg : State -> msg
 | 
				
			||||||
 | 
					    , columns : List (Column data msg)
 | 
				
			||||||
 | 
					    , customizations : Customizations data msg
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  -> Config data msg
 | 
				
			||||||
 | 
					customConfig { toId, toMsg, columns, customizations } =
 | 
				
			||||||
 | 
					  Config
 | 
				
			||||||
 | 
					    { toId = toId
 | 
				
			||||||
 | 
					    , toMsg = toMsg
 | 
				
			||||||
 | 
					    , columns = List.map (\(Column cData) -> cData) columns
 | 
				
			||||||
 | 
					    , customizations = customizations
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| There are quite a lot of ways to customize the `<table>` tag. You can add
 | 
				
			||||||
 | 
					a `<caption>` which can be styled via CSS. You can do crazy stuff with
 | 
				
			||||||
 | 
					`<thead>` to group columns in weird ways. You can have a `<tfoot>` tag for
 | 
				
			||||||
 | 
					summaries of various columns. And maybe you want to put attributes on `<tbody>`
 | 
				
			||||||
 | 
					or on particular rows in the body. All these customizations are available to you.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Note:** The level of craziness possible in `<thead>` and `<tfoot>` are so
 | 
				
			||||||
 | 
					high that I could not see how to provide the full functionality *and* make it
 | 
				
			||||||
 | 
					impossible to do bad stuff. So just be aware of that, and share any stories
 | 
				
			||||||
 | 
					you have. Stories make it possible to design better!
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias Cusomizations data msg =
 | 
				
			||||||
 | 
					  { tableAttrs : List (Attribute msg)
 | 
				
			||||||
 | 
					  , caption : Maybe (HtmlDetails msg)
 | 
				
			||||||
 | 
					  , thead : List (String, Status, Attribute msg) -> HtmlDetails msg
 | 
				
			||||||
 | 
					  , tfoot : Maybe (HtmlDetails msg)
 | 
				
			||||||
 | 
					  , tbodyAttrs : List (Attribute msg)
 | 
				
			||||||
 | 
					  , rowAttrs : data -> List (Attribute msg)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Sometimes you must use a `<td>` tag, but the attributes and children are up
 | 
				
			||||||
 | 
					to you. This type lets you specify all the details of an HTML node except the
 | 
				
			||||||
 | 
					tag name.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias HtmlDetails msg =
 | 
				
			||||||
 | 
					  { attributes : List (Attribute msg)
 | 
				
			||||||
 | 
					  , children : List (Html msg)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defaultCustomizations : Customizations data msg
 | 
				
			||||||
 | 
					defaultCustomizations =
 | 
				
			||||||
 | 
					  { tableAttrs = []
 | 
				
			||||||
 | 
					  , caption = Nothing
 | 
				
			||||||
 | 
					  , thead = simpleThead
 | 
				
			||||||
 | 
					  , tfoot = Nothing
 | 
				
			||||||
 | 
					  , tbodyAttrs = []
 | 
				
			||||||
 | 
					  , rowAttrs = simpleRowAttrs
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					simpleThead : List (String, Status, Attribute msg) -> HtmlDetails msg
 | 
				
			||||||
 | 
					simpleThead headers =
 | 
				
			||||||
 | 
					  HtmlDetails [] (List.map simpleTheadHelp headers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					simpleTheadHelp : ( String, Status, Attribute msg ) -> Html msg
 | 
				
			||||||
 | 
					simpleTheadHelp (name, status, onClick) =
 | 
				
			||||||
 | 
					  let
 | 
				
			||||||
 | 
					    content =
 | 
				
			||||||
 | 
					      case status of
 | 
				
			||||||
 | 
					        Unsortable ->
 | 
				
			||||||
 | 
					          [ text name ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Sortable selected ->
 | 
				
			||||||
 | 
					          [ text name
 | 
				
			||||||
 | 
					          , if selected then darkGrey "↓" else lightGrey "↓"
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Reversable Nothing ->
 | 
				
			||||||
 | 
					          [ text name
 | 
				
			||||||
 | 
					          , lightGrey "↕"
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Reversable (Just isReversed) ->
 | 
				
			||||||
 | 
					          [ text name
 | 
				
			||||||
 | 
					          , darkGrey (if isReversed then "↑" else "↓")
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					  in
 | 
				
			||||||
 | 
					    Html.th [ onClick ] content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					darkGrey : String -> Html msg
 | 
				
			||||||
 | 
					darkGrey symbol =
 | 
				
			||||||
 | 
					  Html.span [ Attr.style [("color", "#ccc")] ] [ Html.text (" " ++ symbol) ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lightGrey : String -> Html msg
 | 
				
			||||||
 | 
					lightGrey symbol =
 | 
				
			||||||
 | 
					  Html.span [ Attr.style [("color", "#999")] ] [ Html.text (" " ++ symbol) ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					simpleRowAttrs : data -> Attribute msg
 | 
				
			||||||
 | 
					simpleRowAttrs _ =
 | 
				
			||||||
 | 
					  []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Status
 | 
				
			||||||
 | 
					  = Unsortable
 | 
				
			||||||
 | 
					  | Sortable Bool
 | 
				
			||||||
 | 
					  | Reversable (Maybe Bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- COLUMNS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Describes how to turn `data` into a column in your table.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type Column data msg =
 | 
				
			||||||
 | 
					  Column (ColumnData data msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type alias ColumnData data msg =
 | 
				
			||||||
 | 
					  { name : String
 | 
				
			||||||
 | 
					  , viewData : data -> HtmlDetails msg
 | 
				
			||||||
 | 
					  , sorter : Sorter data
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-|-}
 | 
				
			||||||
 | 
					stringColumn : String -> (data -> String) -> Column data msg
 | 
				
			||||||
 | 
					stringColumn name toStr =
 | 
				
			||||||
 | 
					  Column
 | 
				
			||||||
 | 
					    { name = name
 | 
				
			||||||
 | 
					    , viewData = textDetails << toStr
 | 
				
			||||||
 | 
					    , sorter = increasingOrDecreasingBy toStr
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-|-}
 | 
				
			||||||
 | 
					intColumn : String -> (data -> Int) -> Column data msg
 | 
				
			||||||
 | 
					intColumn name toInt =
 | 
				
			||||||
 | 
					  Column
 | 
				
			||||||
 | 
					    { name = name
 | 
				
			||||||
 | 
					    , viewData = textDetails << toString << toInt
 | 
				
			||||||
 | 
					    , sorter = increasingOrDecreasingBy toInt
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-|-}
 | 
				
			||||||
 | 
					floatColumn : String -> (data -> Float) -> Column data msg
 | 
				
			||||||
 | 
					floatColumn name toFloat =
 | 
				
			||||||
 | 
					  Column
 | 
				
			||||||
 | 
					    { name = name
 | 
				
			||||||
 | 
					    , viewData = textDetails << toString << toFloat
 | 
				
			||||||
 | 
					    , sorter = increasingOrDecreasingBy toFloat
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					textDetails : String -> HtmlDetails msg
 | 
				
			||||||
 | 
					textDetails str =
 | 
				
			||||||
 | 
					  HtmlDetails [] [ Html.text str ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Perhaps the basic columns are not quite what you want. Maybe you want to
 | 
				
			||||||
 | 
					display monetary values in thousands of dollars, and `floatColumn` does not
 | 
				
			||||||
 | 
					quite cut it. You could define a custom column like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import Table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dollarColumn : String -> (data -> Float) -> Column data msg
 | 
				
			||||||
 | 
					    dollarColumn name toDollars =
 | 
				
			||||||
 | 
					      Table.customColumn
 | 
				
			||||||
 | 
					        { name = name
 | 
				
			||||||
 | 
					        , viewData = \data -> viewDollars (toDollars data)
 | 
				
			||||||
 | 
					        , sorter = Table.decreasingBy toDollars
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    viewDollars : Float -> String
 | 
				
			||||||
 | 
					    viewDollars dollars =
 | 
				
			||||||
 | 
					      "$" ++ toString (round (dollars / 1000)) ++ "k"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `viewData` field means we will displays the number `12345.67` as `$12k`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `sorter` field specifies how the column can be sorted. In `dollarColumn` we
 | 
				
			||||||
 | 
					are saying that it can *only* be shown from highest-to-lowest monetary value.
 | 
				
			||||||
 | 
					More about sorters soon!
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					customColumn
 | 
				
			||||||
 | 
					  : { name : String
 | 
				
			||||||
 | 
					    , viewData : data -> String
 | 
				
			||||||
 | 
					    , sorter : Sorter data
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  -> Column data msg
 | 
				
			||||||
 | 
					customColumn { name, viewData, sorter } =
 | 
				
			||||||
 | 
					  Column <|
 | 
				
			||||||
 | 
					    ColumnData name (textDetails << viewData) sorter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| It is *possible* that you want something crazier than `customColumn`. In
 | 
				
			||||||
 | 
					that unlikely scenario, this function lets you have full control over the
 | 
				
			||||||
 | 
					attributes and children of each `<td>` cell in this column.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So maybe you want to a dollars column, and the dollar signs should be green.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import Html exposing (Html, Attribute, span, text)
 | 
				
			||||||
 | 
					    import Html.Attributes exposing (style)
 | 
				
			||||||
 | 
					    import Table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dollarColumn : String -> (data -> Float) -> Column data msg
 | 
				
			||||||
 | 
					    dollarColumn name toDollars =
 | 
				
			||||||
 | 
					      Table.veryCustomColumn
 | 
				
			||||||
 | 
					        { name = name
 | 
				
			||||||
 | 
					        , viewData = \data -> viewDollars (toDollars data)
 | 
				
			||||||
 | 
					        , sorter = Table.decreasingBy toDollars
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    viewDollars : Float -> Table.HtmlDetails msg
 | 
				
			||||||
 | 
					    viewDollars dollars =
 | 
				
			||||||
 | 
					      Table.HtmlDetails []
 | 
				
			||||||
 | 
					        [ span [ style [("color","green")] ] [ text "$" ]
 | 
				
			||||||
 | 
					        , text (toString (round (dollars / 1000)) ++ "k")
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					veryCustomColumn
 | 
				
			||||||
 | 
					  : { name : String
 | 
				
			||||||
 | 
					    , viewData : data -> HtmlDetails msg
 | 
				
			||||||
 | 
					    , sorter : Sorter data
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  -> Column data msg
 | 
				
			||||||
 | 
					veryCustomColumn =
 | 
				
			||||||
 | 
					  Column
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- VIEW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Take a list of data and turn it into a table. The `Config` argument is the
 | 
				
			||||||
 | 
					configuration for the table. It describes the columns that we want to show. The
 | 
				
			||||||
 | 
					`State` argument describes which column we are sorting by at the moment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Note:** The `State` and `List data` should live in your `Model`. The `Config`
 | 
				
			||||||
 | 
					for the table belongs in your `view` code. I very strongly recommend against
 | 
				
			||||||
 | 
					putting `Config` in your model. Describe any potential table configurations
 | 
				
			||||||
 | 
					statically, and look for a different library if you need something crazier than
 | 
				
			||||||
 | 
					that.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					view : Config data msg -> State -> List data -> Html msg
 | 
				
			||||||
 | 
					view (Config { toId, toMsg, columns, customizations }) state data =
 | 
				
			||||||
 | 
					  let
 | 
				
			||||||
 | 
					    sortedData =
 | 
				
			||||||
 | 
					      sort state columns data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    theadDetails =
 | 
				
			||||||
 | 
					      customizations.thead (List.map Debug.crash columns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    thead =
 | 
				
			||||||
 | 
					      Html.thead theadDetails.attributes theadDetails.children
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tbody =
 | 
				
			||||||
 | 
					      List.map (viewRow toId columns) sortedData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    withFoot =
 | 
				
			||||||
 | 
					      case customizations.tfoot of
 | 
				
			||||||
 | 
					        Nothing ->
 | 
				
			||||||
 | 
					          tbody
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Just { attributes, children } ->
 | 
				
			||||||
 | 
					          Html.tfoot attributes children :: tbody
 | 
				
			||||||
 | 
					  in
 | 
				
			||||||
 | 
					    Html.table customizations.tableAttrs <|
 | 
				
			||||||
 | 
					      case customizations.caption of
 | 
				
			||||||
 | 
					        Nothing ->
 | 
				
			||||||
 | 
					          thead :: withFoot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Just { attributes, children } ->
 | 
				
			||||||
 | 
					          Html.caption attributes children :: thead :: withFoot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					viewHeader : List (ColumnData a msg) -> (State -> msg) -> State -> Html msg
 | 
				
			||||||
 | 
					viewHeader columnData toMsg state =
 | 
				
			||||||
 | 
					  Html.tr [] (List.map (lazy3 viewHeaderHelp toMsg state) columnData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					viewHeaderHelp : (State -> msg) -> State -> ColumnData a msg -> Html msg
 | 
				
			||||||
 | 
					viewHeaderHelp toMsg state ({name} as column) =
 | 
				
			||||||
 | 
					  let
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Html.th
 | 
				
			||||||
 | 
					    [ class (String.join " " classes)
 | 
				
			||||||
 | 
					    , onClick name state toMsg
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    [ html
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					onClick : String -> State -> (State -> msg) -> Attribute msg
 | 
				
			||||||
 | 
					onClick name (State selectedColumn isReversed) toMsg =
 | 
				
			||||||
 | 
					  E.on "click" <| Json.map toMsg <|
 | 
				
			||||||
 | 
					    Json.object2
 | 
				
			||||||
 | 
					      State
 | 
				
			||||||
 | 
					      (Json.succed name)
 | 
				
			||||||
 | 
					      (Json.succed (name == selectedColumn && not isReversed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					descendingClass : Attribute msg
 | 
				
			||||||
 | 
					descendingClass =
 | 
				
			||||||
 | 
					  class "elm-table-selected elm-table-descending"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ascendingClass : Attribute msg
 | 
				
			||||||
 | 
					ascendingClass =
 | 
				
			||||||
 | 
					  class "elm-table-selected elm-table-ascending"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					viewRow : (a -> String) -> List (ColumnData a msg) -> a -> ( String, Html msg )
 | 
				
			||||||
 | 
					viewRow toId columnData entry =
 | 
				
			||||||
 | 
					  ( toId entry
 | 
				
			||||||
 | 
					  , lazy2 viewRowHelp columnData entry
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					viewRowHelp : List (ColumnData a msg) -> a -> Html msg
 | 
				
			||||||
 | 
					viewRowHelp columnData entry =
 | 
				
			||||||
 | 
					  Html.tr [] (List.map (\{toCell} -> Html.td [] [ toCell entry ]) columnData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- SORTING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sort : State -> List (ColumnData data msg) -> List data -> List data
 | 
				
			||||||
 | 
					sort (State selectedColumn isReversed) columnData data =
 | 
				
			||||||
 | 
					  case findSorter selectedColumn columnData of
 | 
				
			||||||
 | 
					    Nothing ->
 | 
				
			||||||
 | 
					      data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Just sorter ->
 | 
				
			||||||
 | 
					      applySorter isReversed sorter data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					applySorter : Bool -> Sorter data -> List data -> List data
 | 
				
			||||||
 | 
					applySorter isReversed sorter data =
 | 
				
			||||||
 | 
					  case sorter of
 | 
				
			||||||
 | 
					    None ->
 | 
				
			||||||
 | 
					      data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Increasing sort ->
 | 
				
			||||||
 | 
					      sort data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Decreasing sort ->
 | 
				
			||||||
 | 
					      List.reverse (sort data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IncOrDec sort ->
 | 
				
			||||||
 | 
					      if isReversed then List.reverse (sort data) else sort data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DecOrInc sort ->
 | 
				
			||||||
 | 
					      if isReversed then sort data else List.reverse (sort data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					findSorter : String -> List (ColumnData data msg) -> Maybe (Sorter data)
 | 
				
			||||||
 | 
					findSorter selectedColumn columnData =
 | 
				
			||||||
 | 
					  case columnData of
 | 
				
			||||||
 | 
					    [] ->
 | 
				
			||||||
 | 
					      Nothing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {name, sorter} :: remainingColumnData ->
 | 
				
			||||||
 | 
					      if name == selectedColumn then
 | 
				
			||||||
 | 
					        sorter
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					        findSorter selectedColumn remainingColumnData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- SORTERS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Specifies a particular way of sorting data.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type Sorter data
 | 
				
			||||||
 | 
					  = None
 | 
				
			||||||
 | 
					  | Increasing (List data -> List data)
 | 
				
			||||||
 | 
					  | Decreasing (List data -> List data)
 | 
				
			||||||
 | 
					  | IncOrDec (List data -> List data)
 | 
				
			||||||
 | 
					  | DecOrInc (List data -> List data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| A sorter for columns that are unsortable. Maybe you have a column in your
 | 
				
			||||||
 | 
					table for delete buttons that delete the row. It would not make any sense to
 | 
				
			||||||
 | 
					sort based on that column.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					unsortable : Sorter data
 | 
				
			||||||
 | 
					unsortable =
 | 
				
			||||||
 | 
					  None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Create a sorter that can only display the data in increasing order. If we
 | 
				
			||||||
 | 
					want a table of people, sorted alphabetically by name, we would say this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sorter : Sorter { a | name : comparable }
 | 
				
			||||||
 | 
					    sorter =
 | 
				
			||||||
 | 
					      increasingBy .name
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					increasingBy : (data -> comparable) -> Sorter data
 | 
				
			||||||
 | 
					increasingBy toComparable =
 | 
				
			||||||
 | 
					  Increasing (List.sortBy toComparable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Create a sorter that can only display the data in decreasing order. If we
 | 
				
			||||||
 | 
					want a table of countries, sorted by population from highest to lowest, we
 | 
				
			||||||
 | 
					would say this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sorter : Sorter { a | population : comparable }
 | 
				
			||||||
 | 
					    sorter =
 | 
				
			||||||
 | 
					      decreasingBy .population
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					decreasingBy : (data -> comparable) -> Sorter data
 | 
				
			||||||
 | 
					decreasingBy toComparable =
 | 
				
			||||||
 | 
					  Decreasing (List.sortBy toComparable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Sometimes you want to be able to sort data in increasing *or* decreasing
 | 
				
			||||||
 | 
					order. Maybe you have a bunch of data about orange juice, and you want to know
 | 
				
			||||||
 | 
					both which has the most sugar, and which has the least sugar. Both interesting!
 | 
				
			||||||
 | 
					This function lets you see both, starting with decreasing order.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sorter : Sorter { a | sugar : comparable }
 | 
				
			||||||
 | 
					    sorter =
 | 
				
			||||||
 | 
					      decreasingOrIncreasingBy .sugar
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					decreasingOrIncreasingBy : (data -> comparable) -> Sorter data
 | 
				
			||||||
 | 
					decreasingOrIncreasingBy toComparable =
 | 
				
			||||||
 | 
					  DecOrInc (List.sortBy toComparable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Sometimes you want to be able to sort data in increasing *or* decreasing
 | 
				
			||||||
 | 
					order. Maybe you have race times for the 100 meter sprint. This function lets
 | 
				
			||||||
 | 
					sort by best time by default, but also see the other order.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sorter : Sorter { a | time : comparable }
 | 
				
			||||||
 | 
					    sorter =
 | 
				
			||||||
 | 
					      increasingOrDecreasingBy .time
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					increasingOrDecreasingBy : (data -> comparable) -> Sorter data
 | 
				
			||||||
 | 
					increasingOrDecreasingBy toComparable =
 | 
				
			||||||
 | 
					  IncOrDec (List.sortBy toComparable)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user