github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/web/elm/src/Dashboard/SearchBar.elm (about)

     1  module Dashboard.SearchBar exposing
     2      ( handleDelivery
     3      , searchInputId
     4      , update
     5      , view
     6      )
     7  
     8  import Application.Models exposing (Session)
     9  import Array
    10  import Concourse
    11  import Concourse.PipelineStatus
    12      exposing
    13          ( PipelineStatus(..)
    14          , StatusDetails(..)
    15          )
    16  import Dashboard.Group.Models exposing (Pipeline)
    17  import Dashboard.Models exposing (Dropdown(..), Model)
    18  import Dashboard.Styles as Styles
    19  import Dict exposing (Dict)
    20  import EffectTransformer exposing (ET)
    21  import FetchResult exposing (FetchResult)
    22  import Html exposing (Html)
    23  import Html.Attributes exposing (attribute, id, placeholder, value)
    24  import Html.Events exposing (onBlur, onClick, onFocus, onInput, onMouseDown)
    25  import Keyboard
    26  import Message.Callback exposing (Callback(..))
    27  import Message.Effects exposing (Effect(..))
    28  import Message.Message exposing (DomID(..), Message(..))
    29  import Message.Subscription exposing (Delivery(..))
    30  import Routes
    31  import ScreenSize exposing (ScreenSize)
    32  import Set
    33  
    34  
    35  searchInputId : String
    36  searchInputId =
    37      "search-input-field"
    38  
    39  
    40  update : Session -> Message -> ET Model
    41  update session msg ( model, effects ) =
    42      case msg of
    43          Click ShowSearchButton ->
    44              showSearchInput session ( model, effects )
    45  
    46          Click ClearSearchButton ->
    47              ( { model | query = "" }
    48              , effects
    49                  ++ [ Focus searchInputId
    50                     , ModifyUrl <|
    51                          Routes.toString <|
    52                              Routes.Dashboard
    53                                  { searchType = Routes.Normal ""
    54                                  , dashboardView = model.dashboardView
    55                                  }
    56                     ]
    57              )
    58  
    59          FilterMsg query ->
    60              ( { model | query = query }
    61              , effects
    62                  ++ [ Focus searchInputId
    63                     , ModifyUrl <|
    64                          Routes.toString <|
    65                              Routes.Dashboard
    66                                  { searchType = Routes.Normal query
    67                                  , dashboardView = model.dashboardView
    68                                  }
    69                     ]
    70              )
    71  
    72          FocusMsg ->
    73              ( { model | dropdown = Shown Nothing }, effects )
    74  
    75          BlurMsg ->
    76              ( { model | dropdown = Hidden }, effects )
    77  
    78          _ ->
    79              ( model, effects )
    80  
    81  
    82  showSearchInput : { a | screenSize : ScreenSize } -> ET Model
    83  showSearchInput session ( model, effects ) =
    84      if model.highDensity then
    85          ( model, effects )
    86  
    87      else
    88          let
    89              isDropDownHidden =
    90                  model.dropdown == Hidden
    91  
    92              isMobile =
    93                  session.screenSize == ScreenSize.Mobile
    94          in
    95          if isDropDownHidden && isMobile && model.query == "" then
    96              ( { model | dropdown = Shown Nothing }
    97              , effects ++ [ Focus searchInputId ]
    98              )
    99  
   100          else
   101              ( model, effects )
   102  
   103  
   104  screenResize : Float -> Model -> Model
   105  screenResize width model =
   106      let
   107          newSize =
   108              ScreenSize.fromWindowSize width
   109      in
   110      case newSize of
   111          ScreenSize.Desktop ->
   112              { model | dropdown = Hidden }
   113  
   114          ScreenSize.BigDesktop ->
   115              { model | dropdown = Hidden }
   116  
   117          ScreenSize.Mobile ->
   118              model
   119  
   120  
   121  handleDelivery : Delivery -> ET Model
   122  handleDelivery delivery ( model, effects ) =
   123      case delivery of
   124          WindowResized width _ ->
   125              ( screenResize width model, effects )
   126  
   127          KeyDown keyEvent ->
   128              let
   129                  options =
   130                      dropdownOptions model
   131              in
   132              case keyEvent.code of
   133                  Keyboard.ArrowUp ->
   134                      ( { model
   135                          | dropdown =
   136                              arrowUp options model.dropdown
   137                        }
   138                      , effects
   139                      )
   140  
   141                  Keyboard.ArrowDown ->
   142                      ( { model
   143                          | dropdown =
   144                              arrowDown options model.dropdown
   145                        }
   146                      , effects
   147                      )
   148  
   149                  Keyboard.Enter ->
   150                      case model.dropdown of
   151                          Shown (Just idx) ->
   152                              let
   153                                  selectedItem =
   154                                      options
   155                                          |> Array.fromList
   156                                          |> Array.get idx
   157                                          |> Maybe.withDefault
   158                                              model.query
   159                              in
   160                              ( { model
   161                                  | dropdown = Shown Nothing
   162                                  , query = selectedItem
   163                                }
   164                              , [ ModifyUrl <|
   165                                      Routes.toString <|
   166                                          Routes.Dashboard
   167                                              { searchType = Routes.Normal selectedItem
   168                                              , dashboardView = model.dashboardView
   169                                              }
   170                                ]
   171                              )
   172  
   173                          Shown Nothing ->
   174                              ( model, effects )
   175  
   176                          Hidden ->
   177                              ( model, effects )
   178  
   179                  Keyboard.Escape ->
   180                      ( model, effects ++ [ Blur searchInputId ] )
   181  
   182                  Keyboard.Slash ->
   183                      ( model
   184                      , if keyEvent.shiftKey then
   185                          effects
   186  
   187                        else
   188                          effects ++ [ Focus searchInputId ]
   189                      )
   190  
   191                  -- any other keycode
   192                  _ ->
   193                      ( model, effects )
   194  
   195          _ ->
   196              ( model, effects )
   197  
   198  
   199  arrowUp : List a -> Dropdown -> Dropdown
   200  arrowUp options dropdown =
   201      case dropdown of
   202          Shown Nothing ->
   203              let
   204                  lastItem =
   205                      List.length options - 1
   206              in
   207              Shown (Just lastItem)
   208  
   209          Shown (Just idx) ->
   210              let
   211                  newSelection =
   212                      modBy (List.length options) (idx - 1)
   213              in
   214              Shown (Just newSelection)
   215  
   216          Hidden ->
   217              Hidden
   218  
   219  
   220  arrowDown : List a -> Dropdown -> Dropdown
   221  arrowDown options dropdown =
   222      case dropdown of
   223          Shown Nothing ->
   224              Shown (Just 0)
   225  
   226          Shown (Just idx) ->
   227              let
   228                  newSelection =
   229                      modBy (List.length options) (idx + 1)
   230              in
   231              Shown (Just newSelection)
   232  
   233          Hidden ->
   234              Hidden
   235  
   236  
   237  view :
   238      { a | screenSize : ScreenSize }
   239      ->
   240          { b
   241              | query : String
   242              , dropdown : Dropdown
   243              , teams : FetchResult (List Concourse.Team)
   244              , highDensity : Bool
   245              , pipelines : Maybe (Dict String (List Pipeline))
   246          }
   247      -> Html Message
   248  view session ({ query, dropdown, pipelines } as params) =
   249      let
   250          isDropDownHidden =
   251              dropdown == Hidden
   252  
   253          isMobile =
   254              session.screenSize == ScreenSize.Mobile
   255  
   256          noPipelines =
   257              pipelines
   258                  |> Maybe.withDefault Dict.empty
   259                  |> Dict.values
   260                  |> List.all List.isEmpty
   261  
   262          clearSearchButton =
   263              if String.length query > 0 then
   264                  [ Html.div
   265                      ([ id "search-clear"
   266                       , onClick <| Click ClearSearchButton
   267                       ]
   268                          ++ Styles.searchClearButton
   269                      )
   270                      []
   271                  ]
   272  
   273              else
   274                  []
   275      in
   276      if noPipelines then
   277          Html.text ""
   278  
   279      else if isDropDownHidden && isMobile && query == "" then
   280          Html.div
   281              (Styles.showSearchContainer
   282                  { screenSize = session.screenSize
   283                  , highDensity = params.highDensity
   284                  }
   285              )
   286              [ Html.div
   287                  ([ id "show-search-button"
   288                   , onClick <| Click ShowSearchButton
   289                   ]
   290                      ++ Styles.searchButton
   291                  )
   292                  []
   293              ]
   294  
   295      else
   296          Html.div
   297              (id "search-container" :: Styles.searchContainer session.screenSize)
   298              (Html.input
   299                  ([ id searchInputId
   300                   , placeholder "filter pipelines by name, status, or team"
   301                   , attribute "autocomplete" "off"
   302                   , value query
   303                   , onFocus FocusMsg
   304                   , onBlur BlurMsg
   305                   , onInput FilterMsg
   306                   ]
   307                      ++ Styles.searchInput
   308                          session.screenSize
   309                          (String.length query > 0)
   310                  )
   311                  []
   312                  :: clearSearchButton
   313                  ++ viewDropdownItems session params
   314              )
   315  
   316  
   317  viewDropdownItems :
   318      { a
   319          | screenSize : ScreenSize
   320      }
   321      ->
   322          { b
   323              | query : String
   324              , dropdown : Dropdown
   325              , teams : FetchResult (List Concourse.Team)
   326              , pipelines : Maybe (Dict String (List Pipeline))
   327          }
   328      -> List (Html Message)
   329  viewDropdownItems { screenSize } ({ dropdown, query } as model) =
   330      case dropdown of
   331          Hidden ->
   332              []
   333  
   334          Shown selectedIdx ->
   335              let
   336                  dropdownItem : Int -> String -> Html Message
   337                  dropdownItem idx text =
   338                      Html.li
   339                          (onMouseDown (FilterMsg text)
   340                              :: Styles.dropdownItem
   341                                  (Just idx == selectedIdx)
   342                                  (String.length query > 0)
   343                          )
   344                          [ Html.text text ]
   345              in
   346              [ Html.ul
   347                  (id "search-dropdown" :: Styles.dropdownContainer screenSize)
   348                  (List.indexedMap dropdownItem (dropdownOptions model))
   349              ]
   350  
   351  
   352  dropdownOptions :
   353      { a
   354          | query : String
   355          , teams : FetchResult (List Concourse.Team)
   356          , pipelines : Maybe (Dict String (List Pipeline))
   357      }
   358      -> List String
   359  dropdownOptions { query, teams, pipelines } =
   360      case String.trim query of
   361          "" ->
   362              [ "status: ", "team: " ]
   363  
   364          "status:" ->
   365              [ "status: paused"
   366              , "status: pending"
   367              , "status: failed"
   368              , "status: errored"
   369              , "status: aborted"
   370              , "status: running"
   371              , "status: succeeded"
   372              ]
   373  
   374          "team:" ->
   375              Set.union
   376                  (teams
   377                      |> FetchResult.withDefault []
   378                      |> List.map .name
   379                      |> Set.fromList
   380                  )
   381                  (pipelines
   382                      |> Maybe.withDefault Dict.empty
   383                      |> Dict.keys
   384                      |> Set.fromList
   385                  )
   386                  |> Set.toList
   387                  |> List.take 10
   388                  |> List.map (\teamName -> "team: " ++ teamName)
   389  
   390          _ ->
   391              []