github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/web/elm/src/Pipeline/PinMenu/PinMenu.elm (about) 1 module Pipeline.PinMenu.PinMenu exposing 2 ( TableRow 3 , View 4 , pinMenu 5 , update 6 , viewPinMenu 7 ) 8 9 import Colors 10 import Concourse 11 import Dict 12 import EffectTransformer exposing (ET) 13 import HoverState 14 import Html exposing (Html) 15 import Html.Attributes exposing (id, style) 16 import Html.Events exposing (onClick, onMouseEnter, onMouseLeave) 17 import Message.Message exposing (DomID(..), Message(..)) 18 import Pipeline.PinMenu.Styles as Styles 19 import Pipeline.PinMenu.Views as Views 20 import Routes 21 import SideBar.Styles as SS 22 import Views.Styles 23 24 25 type alias Model b = 26 { b 27 | fetchedResources : Maybe (List Concourse.Resource) 28 , pipelineLocator : Concourse.PipelineIdentifier 29 , pinMenuExpanded : Bool 30 } 31 32 33 type alias View = 34 { hoverable : Bool 35 , clickable : Bool 36 , background : Views.Background 37 , opacity : SS.Opacity 38 , badge : Maybe Badge 39 , dropdown : Maybe Dropdown 40 } 41 42 43 type alias Dropdown = 44 { position : Views.Position 45 , items : List DropdownItem 46 } 47 48 49 type alias DropdownItem = 50 { title : Text 51 , table : List TableRow 52 , paddingPx : Int 53 , background : String 54 , onClick : Message 55 , hoverable : Bool 56 } 57 58 59 type alias Badge = 60 { color : String 61 , diameterPx : Int 62 , position : Views.Position 63 , text : String 64 } 65 66 67 type alias Text = 68 { content : String 69 , fontWeight : String 70 , color : String 71 } 72 73 74 type alias TableRow = 75 { left : String 76 , right : String 77 , color : String 78 } 79 80 81 update : Message -> ET (Model b) 82 update message ( model, effects ) = 83 case message of 84 Click PinIcon -> 85 ( { model | pinMenuExpanded = not model.pinMenuExpanded } 86 , effects 87 ) 88 89 _ -> 90 ( model, effects ) 91 92 93 pinMenu : 94 { a | hovered : HoverState.HoverState } 95 -> Model b 96 -> View 97 pinMenu { hovered } model = 98 let 99 pinnedResources = 100 getPinnedResources model.fetchedResources 101 102 pipeline = 103 model.pipelineLocator 104 105 pinCount = 106 List.length pinnedResources 107 108 hasPinnedResources = 109 pinCount > 0 110 111 isHovered = 112 hovered == HoverState.Hovered PinIcon 113 in 114 { hoverable = hasPinnedResources 115 , clickable = hasPinnedResources 116 , opacity = 117 if isHovered || model.pinMenuExpanded then 118 SS.Bright 119 120 else if hasPinnedResources then 121 SS.GreyedOut 122 123 else 124 SS.Dim 125 , background = 126 if model.pinMenuExpanded then 127 Views.Light 128 129 else 130 Views.Dark 131 , badge = 132 if hasPinnedResources then 133 Just 134 { color = Colors.pinned 135 , diameterPx = 15 136 , position = Views.TopRight (Views.Px 10) (Views.Px 10) 137 , text = String.fromInt pinCount 138 } 139 140 else 141 Nothing 142 , dropdown = 143 if model.pinMenuExpanded then 144 Just 145 { position = 146 Views.TopRight (Views.Percent 100) (Views.Percent 0) 147 , items = 148 List.map 149 (\( resourceName, pinnedVersion ) -> 150 { title = 151 { content = resourceName 152 , fontWeight = Views.Styles.fontWeightDefault 153 , color = Colors.text 154 } 155 , table = 156 pinnedVersion 157 |> Dict.toList 158 |> List.map 159 (\( k, v ) -> 160 { left = k 161 , right = v 162 , color = Colors.text 163 } 164 ) 165 , paddingPx = 10 166 , background = 167 if 168 hovered 169 == HoverState.Hovered 170 (PinMenuDropDown resourceName) 171 then 172 Colors.pinMenuHover 173 174 else 175 Colors.pinMenuBackground 176 , hoverable = True 177 , onClick = 178 GoToRoute <| 179 Routes.Resource 180 { id = 181 { teamName = pipeline.teamName 182 , pipelineName = pipeline.pipelineName 183 , resourceName = resourceName 184 } 185 , page = Nothing 186 } 187 } 188 ) 189 pinnedResources 190 } 191 192 else 193 Nothing 194 } 195 196 197 viewPinMenu : 198 { a | hovered : HoverState.HoverState } 199 -> Model b 200 -> Html Message 201 viewPinMenu session m = 202 pinMenu session m 203 |> viewView 204 205 206 getPinnedResources : Maybe (List Concourse.Resource) -> List ( String, Concourse.Version ) 207 getPinnedResources fetchedResources = 208 case fetchedResources of 209 Nothing -> 210 [] 211 212 Just resources -> 213 resources 214 |> List.filterMap 215 (\r -> 216 Maybe.map (\v -> ( r.name, v )) r.pinnedVersion 217 ) 218 219 220 viewView : View -> Html Message 221 viewView view = 222 Html.div 223 (([ ( id "pin-icon", True ) 224 , ( onMouseEnter <| Hover <| Just PinIcon, view.hoverable ) 225 , ( onMouseLeave <| Hover Nothing, view.hoverable ) 226 , ( onClick <| Click PinIcon, view.clickable ) 227 ] 228 |> List.filter Tuple.second 229 |> List.map Tuple.first 230 ) 231 ++ Styles.pinIconBackground view 232 ) 233 (Html.div 234 (Styles.pinIcon view) 235 [] 236 :: ([ Maybe.map viewBadge view.badge 237 , Maybe.map viewDropdown view.dropdown 238 ] 239 |> List.filterMap identity 240 ) 241 ) 242 243 244 viewBadge : Badge -> Html Message 245 viewBadge badge = 246 Html.div 247 (id "pin-badge" :: Styles.pinBadge badge) 248 [ Html.div [] [ Html.text badge.text ] ] 249 250 251 viewDropdown : Dropdown -> Html Message 252 viewDropdown dropdown = 253 Html.ul 254 (Styles.pinIconDropdown dropdown) 255 (List.map viewDropdownItem dropdown.items) 256 257 258 viewDropdownItem : DropdownItem -> Html Message 259 viewDropdownItem item = 260 Html.li 261 (onClick item.onClick 262 :: (if item.hoverable then 263 [ onMouseEnter <| 264 Hover <| 265 Just <| 266 PinMenuDropDown item.title.content 267 , onMouseLeave <| Hover Nothing 268 ] 269 270 else 271 [] 272 ) 273 ++ Styles.pinIconDropdownItem item 274 ) 275 [ viewTitle item.title 276 , Html.table [] (List.map viewTableRow item.table) 277 ] 278 279 280 viewTitle : Text -> Html Message 281 viewTitle title = 282 Html.div 283 (Styles.title title) 284 [ Html.text title.content ] 285 286 287 viewTableRow : TableRow -> Html Message 288 viewTableRow { left, right, color } = 289 Html.tr 290 [ style "color" color ] 291 [ Html.td [] [ Html.text left ] 292 , Html.td [] [ Html.text right ] 293 ]