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

     1  module Dashboard.PipelineGrid exposing
     2      ( Bounds
     3      , DropArea
     4      , Header
     5      , PipelineCard
     6      , computeFavoritePipelinesLayout
     7      , computeLayout
     8      )
     9  
    10  import Concourse
    11  import Dashboard.Drag exposing (dragPipeline)
    12  import Dashboard.Group.Models exposing (Group, Pipeline)
    13  import Dashboard.Models exposing (DragState(..), DropState(..))
    14  import Dashboard.PipelineGrid.Constants
    15      exposing
    16          ( cardHeight
    17          , cardWidth
    18          , headerHeight
    19          , padding
    20          )
    21  import Dashboard.PipelineGrid.Layout as Layout
    22  import Dict exposing (Dict)
    23  import List.Extra
    24  import Message.Message exposing (DomID(..), DropTarget(..), Message(..))
    25  import UserState exposing (UserState(..))
    26  
    27  
    28  type alias Bounds =
    29      { x : Float
    30      , y : Float
    31      , width : Float
    32      , height : Float
    33      }
    34  
    35  
    36  type alias PipelineCard =
    37      { bounds : Bounds
    38      , pipeline : Pipeline
    39      }
    40  
    41  
    42  type alias DropArea =
    43      { bounds : Bounds
    44      , target : DropTarget
    45      }
    46  
    47  
    48  type alias Header =
    49      { bounds : Bounds
    50      , header : String
    51      }
    52  
    53  
    54  computeLayout :
    55      { dragState : DragState
    56      , dropState : DropState
    57      , pipelineLayers : Dict ( String, String ) (List (List Concourse.JobIdentifier))
    58      , viewportWidth : Float
    59      , viewportHeight : Float
    60      , scrollTop : Float
    61      }
    62      -> Group
    63      ->
    64          { pipelineCards : List PipelineCard
    65          , dropAreas : List DropArea
    66          , height : Float
    67          }
    68  computeLayout params g =
    69      let
    70          orderedPipelines =
    71              case ( params.dragState, params.dropState ) of
    72                  ( Dragging team pipeline, Dropping target ) ->
    73                      if g.teamName == team then
    74                          dragPipeline pipeline target g.pipelines
    75  
    76                      else
    77                          g.pipelines
    78  
    79                  _ ->
    80                      g.pipelines
    81  
    82          numColumns =
    83              max 1 (floor (params.viewportWidth / (cardWidth + padding)))
    84  
    85          rowHeight =
    86              cardHeight + padding
    87  
    88          isVisible_ =
    89              isVisible params.viewportHeight params.scrollTop rowHeight
    90  
    91          cards =
    92              orderedPipelines
    93                  |> previewSizes params.pipelineLayers
    94                  |> List.map Layout.cardSize
    95                  |> Layout.layout numColumns
    96  
    97          numRows =
    98              cards
    99                  |> List.map (\c -> c.row + c.spannedRows - 1)
   100                  |> List.maximum
   101                  |> Maybe.withDefault 1
   102  
   103          totalCardsHeight =
   104              toFloat numRows
   105                  * cardHeight
   106                  + padding
   107                  * toFloat numRows
   108  
   109          cardLookup =
   110              cards
   111                  |> List.map2 Tuple.pair orderedPipelines
   112                  |> List.map (\( pipeline, card ) -> ( pipeline.id, card ))
   113                  |> Dict.fromList
   114  
   115          prevAndCurrentCards =
   116              cards
   117                  |> List.map2 Tuple.pair (Nothing :: (cards |> List.map Just))
   118  
   119          cardBounds =
   120              boundsForCell
   121                  { colGap = padding
   122                  , rowGap = padding
   123                  , offsetX = padding
   124                  , offsetY = 0
   125                  }
   126  
   127          dropAreaBounds =
   128              cardBounds >> (\b -> { b | x = b.x - padding, width = b.width + padding })
   129  
   130          dropAreas =
   131              (prevAndCurrentCards
   132                  |> List.map2 Tuple.pair g.pipelines
   133                  |> List.filter (\( _, ( _, card ) ) -> isVisible_ card)
   134                  |> List.map
   135                      (\( pipeline, ( prevCard, card ) ) ->
   136                          let
   137                              boundsToRightOf otherCard =
   138                                  dropAreaBounds
   139                                      { otherCard
   140                                          | column = otherCard.column + otherCard.spannedColumns
   141                                          , spannedColumns = 1
   142                                      }
   143  
   144                              bounds =
   145                                  case prevCard of
   146                                      Just otherCard ->
   147                                          if
   148                                              (otherCard.row < card.row)
   149                                                  && (otherCard.column + otherCard.spannedColumns <= numColumns)
   150                                          then
   151                                              boundsToRightOf otherCard
   152  
   153                                          else
   154                                              dropAreaBounds card
   155  
   156                                      Nothing ->
   157                                          dropAreaBounds card
   158                          in
   159                          { bounds = bounds, target = Before pipeline.name }
   160                      )
   161              )
   162                  ++ (case List.head (List.reverse (List.map2 Tuple.pair cards g.pipelines)) of
   163                          Just ( lastCard, lastPipeline ) ->
   164                              if not (isVisible_ lastCard) then
   165                                  []
   166  
   167                              else
   168                                  [ { bounds =
   169                                          dropAreaBounds
   170                                              { lastCard
   171                                                  | column = lastCard.column + lastCard.spannedColumns
   172                                                  , spannedColumns = 1
   173                                              }
   174                                    , target = After lastPipeline.name
   175                                    }
   176                                  ]
   177  
   178                          Nothing ->
   179                              []
   180                     )
   181  
   182          pipelineCards =
   183              g.pipelines
   184                  |> List.map
   185                      (\pipeline ->
   186                          cardLookup
   187                              |> Dict.get pipeline.id
   188                              |> Maybe.withDefault
   189                                  { row = 0
   190                                  , column = 0
   191                                  , spannedColumns = 0
   192                                  , spannedRows = 0
   193                                  }
   194                              |> (\card -> ( pipeline, card ))
   195                      )
   196                  |> List.filter (\( _, card ) -> isVisible_ card)
   197                  |> List.map
   198                      (\( pipeline, card ) ->
   199                          { pipeline = pipeline
   200                          , bounds = cardBounds card
   201                          }
   202                      )
   203      in
   204      { pipelineCards = pipelineCards
   205      , dropAreas = dropAreas
   206      , height = totalCardsHeight
   207      }
   208  
   209  
   210  computeFavoritePipelinesLayout :
   211      { pipelineLayers : Dict ( String, String ) (List (List Concourse.JobIdentifier))
   212      , viewportWidth : Float
   213      , viewportHeight : Float
   214      , scrollTop : Float
   215      }
   216      -> List Pipeline
   217      ->
   218          { pipelineCards : List PipelineCard
   219          , headers : List Header
   220          , height : Float
   221          }
   222  computeFavoritePipelinesLayout params pipelines =
   223      let
   224          numColumns =
   225              max 1 (floor (params.viewportWidth / (cardWidth + padding)))
   226  
   227          rowHeight =
   228              cardHeight + headerHeight
   229  
   230          isVisible_ =
   231              isVisible params.viewportHeight params.scrollTop rowHeight
   232  
   233          cards =
   234              pipelines
   235                  |> previewSizes params.pipelineLayers
   236                  |> List.map Layout.cardSize
   237                  |> Layout.layout numColumns
   238  
   239          numRows =
   240              cards
   241                  |> List.map (\c -> c.row + c.spannedRows - 1)
   242                  |> List.maximum
   243                  |> Maybe.withDefault 1
   244  
   245          totalCardsHeight =
   246              toFloat numRows * rowHeight
   247  
   248          cardBounds =
   249              boundsForCell
   250                  { colGap = padding
   251                  , rowGap = headerHeight
   252                  , offsetX = padding
   253                  , offsetY = headerHeight
   254                  }
   255  
   256          pipelineCards =
   257              cards
   258                  |> List.map2 Tuple.pair pipelines
   259                  |> List.filter (\( _, card ) -> isVisible_ card)
   260                  |> List.map
   261                      (\( pipeline, card ) ->
   262                          { pipeline = pipeline
   263                          , bounds = cardBounds card
   264                          }
   265                      )
   266  
   267          headers =
   268              pipelineCards
   269                  |> List.Extra.groupWhile
   270                      (\c1 c2 ->
   271                          (c1.pipeline.teamName == c2.pipeline.teamName)
   272                              && (c1.bounds.y == c2.bounds.y)
   273                      )
   274                  |> List.map
   275                      (\( c, cs ) ->
   276                          ( c
   277                          , case List.Extra.last cs of
   278                              Nothing ->
   279                                  c
   280  
   281                              Just tail ->
   282                                  tail
   283                          )
   284                      )
   285                  |> List.foldl
   286                      (\( first, last ) ( prevTeam, headers_ ) ->
   287                          let
   288                              curTeam =
   289                                  first.pipeline.teamName
   290  
   291                              header =
   292                                  case prevTeam of
   293                                      Nothing ->
   294                                          curTeam
   295  
   296                                      Just prevTeam_ ->
   297                                          if prevTeam_ == curTeam then
   298                                              curTeam ++ " (continued)"
   299  
   300                                          else
   301                                              curTeam
   302                          in
   303                          ( Just curTeam
   304                          , { header = header
   305                            , bounds =
   306                                  { x = first.bounds.x
   307                                  , y = first.bounds.y - headerHeight
   308                                  , width = last.bounds.x + cardWidth - first.bounds.x
   309                                  , height = headerHeight
   310                                  }
   311                            }
   312                              :: headers_
   313                          )
   314                      )
   315                      ( Nothing, [] )
   316                  |> Tuple.second
   317      in
   318      { pipelineCards = pipelineCards
   319      , headers = headers
   320      , height = totalCardsHeight
   321      }
   322  
   323  
   324  previewSizes :
   325      Dict ( String, String ) (List (List Concourse.JobIdentifier))
   326      -> List Pipeline
   327      -> List ( Int, Int )
   328  previewSizes pipelineLayers =
   329      List.map
   330          (\pipeline ->
   331              Dict.get ( pipeline.teamName, pipeline.name ) pipelineLayers
   332                  |> Maybe.withDefault []
   333          )
   334          >> List.map
   335              (\layers ->
   336                  ( List.length layers
   337                  , layers
   338                      |> List.map List.length
   339                      |> List.maximum
   340                      |> Maybe.withDefault 0
   341                  )
   342              )
   343  
   344  
   345  isVisible : Float -> Float -> Float -> { r | row : Int, spannedRows : Int } -> Bool
   346  isVisible viewportHeight scrollTop rowHeight { row, spannedRows } =
   347      let
   348          numRowsVisible =
   349              ceiling (viewportHeight / rowHeight) + 1
   350  
   351          numRowsOffset =
   352              floor (scrollTop / rowHeight)
   353      in
   354      (numRowsOffset < row + spannedRows)
   355          && (row <= numRowsOffset + numRowsVisible)
   356  
   357  
   358  boundsForCell :
   359      { colGap : Float
   360      , rowGap : Float
   361      , offsetX : Float
   362      , offsetY : Float
   363      }
   364      -> Layout.Card
   365      -> Bounds
   366  boundsForCell { colGap, rowGap, offsetX, offsetY } card =
   367      let
   368          colWidth =
   369              cardWidth + colGap
   370  
   371          rowHeight =
   372              cardHeight + rowGap
   373      in
   374      { x = (toFloat card.column - 1) * colWidth + offsetX
   375      , y = (toFloat card.row - 1) * rowHeight + offsetY
   376      , width =
   377          cardWidth
   378              * toFloat card.spannedColumns
   379              + colGap
   380              * (toFloat card.spannedColumns - 1)
   381      , height =
   382          cardHeight
   383              * toFloat card.spannedRows
   384              + rowGap
   385              * (toFloat card.spannedRows - 1)
   386      }