A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add search UI

+260 -31
+3 -3
README.md
··· 31 31 32 32 - [Elm](https://elm-lang.org/) programming language 33 33 - [Haskell](https://docs.haskellstack.org/en/stable/README/) programming language 34 - - [Elm Proofread](https://github.com/icidasset/elm-proofread) documentation tests 35 - - [Devd](https://github.com/cortesi/devd) web server for development 36 - - [Watchexec](https://github.com/watchexec/watchexec) watching for file changes 34 + - [Elm Proofread](https://github.com/icidasset/elm-proofread) documentation tests (optional) 35 + - [Devd](https://github.com/cortesi/devd) web server for development (optional) 36 + - [Watchexec](https://github.com/watchexec/watchexec) watching for file changes (optional) 37 37 38 38 39 39 ```shell
+2
src/Applications/UI/Kit.elm
··· 43 43 { errorBorder = colorKit.base08 44 44 , inputBorder = rgb 225 225 225 45 45 , subtleBorder = rgb 238 238 238 46 + , verySubtleBorder = rgb 248 248 248 46 47 47 48 -- States 48 49 , success = colorKit.base0B ··· 323 324 , T.bg_white 324 325 , T.flex 325 326 , T.flex_column 327 + , T.z_999 326 328 ] 327 329 328 330
+1 -1
src/Applications/UI/List.elm
··· 76 76 77 77 itemStyles : List Css.Style 78 78 itemStyles = 79 - [ Css.borderBottom3 (px 1) solid (Color.toElmCssColor UI.Kit.colors.subtleBorder) ] 79 + [ Css.borderBottom3 (px 1) solid (Color.toElmCssColor UI.Kit.colors.verySubtleBorder) ]
+20 -15
src/Applications/UI/Navigation.elm
··· 65 65 isLastItem = 66 66 idx + 1 == totalItems 67 67 in 68 - slab 69 - Html.a 70 - [ attribute "data-keep-focus" "t" 71 - , css (globalItemStyles isActivePage) 72 - , href (Page.toString page) 68 + chunk 69 + [ T.dib 70 + , ifThenElse isLastItem T.mr0 T.mr1 73 71 ] 74 - [ T.dib 75 - , T.lh_copy 76 - , T.no_underline 77 - , T.pointer 78 - , T.pt2 72 + [ slab 73 + Html.a 74 + [ attribute "data-keep-focus" "t" 75 + , css (globalItemStyles isActivePage) 76 + , href (Page.toString page) 77 + ] 78 + [ T.dib 79 + , T.lh_copy 80 + , T.no_underline 81 + , T.pointer 82 + , T.pt2 79 83 80 - -- 81 - , ifThenElse isActivePage T.bb T.bn 82 - , ifThenElse isLastItem T.mr0 T.mr4 84 + -- 85 + , ifThenElse isActivePage T.bb T.bn 86 + , ifThenElse isLastItem T.mr0 T.mr4 87 + ] 88 + [ text label ] 83 89 ] 84 - [ text label ] 85 90 86 91 87 92 globalColors : { active : Css.Color, border : Css.Color, default : Css.Color } ··· 94 99 95 100 globalStyles : List Css.Style 96 101 globalStyles = 97 - [ Css.fontSize (px 11.5) ] 102 + [ Css.fontSize (px 11.25) ] 98 103 99 104 100 105 globalItemStyles : Bool -> List Css.Style
+212 -12
src/Applications/UI/Tracks.elm
··· 1 1 module UI.Tracks exposing (Model, Msg(..), initialModel, makeParcel, resolveParcel, update, view) 2 2 3 3 import Chunky exposing (..) 4 + import Color 5 + import Color.Ext as Color 6 + import Css 4 7 import Html.Styled as Html exposing (Html, text) 8 + import Html.Styled.Attributes exposing (css, placeholder, title, value) 9 + import Html.Styled.Events exposing (onClick, onInput) 10 + import Html.Styled.Ext exposing (onEnterKey) 11 + import Html.Styled.Lazy exposing (..) 5 12 import Json.Decode 13 + import Material.Icons.Action as Icons 14 + import Material.Icons.Av as Icons 15 + import Material.Icons.Content as Icons 16 + import Material.Icons.Editor as Icons 6 17 import Replying exposing (R3D3) 7 18 import Return3 19 + import Tachyons.Classes as T 8 20 import Tracks exposing (..) 9 21 import Tracks.Collection exposing (..) 10 22 import Tracks.Encoding as Encoding 11 23 import UI.Kit 24 + import UI.Navigation exposing (..) 12 25 import UI.Reply exposing (Reply(..)) 13 26 14 27 ··· 50 63 type Msg 51 64 = Bypass 52 65 ----------------------------------------- 53 - -- Collection, Pt. 1 66 + -- Collection 54 67 ----------------------------------------- 68 + | Add Json.Decode.Value 69 + ----------------------------------------- 70 + -- Favourites 55 71 ----------------------------------------- 56 - -- Collection, Pt. 2 72 + | ToggleFavouritesOnly 57 73 ----------------------------------------- 58 - | Add Json.Decode.Value 74 + -- Search 75 + ----------------------------------------- 76 + | ClearSearch 77 + | Search (Maybe String) 78 + | SetSearchTerm String 59 79 60 80 61 81 update : Msg -> Model -> R3D3 Model Msg Reply ··· 65 85 Return3.withNothing model 66 86 67 87 ----------------------------------------- 68 - -- Collection, Pt. 1 69 - ----------------------------------------- 70 - ----------------------------------------- 71 - -- Collection, Pt. 2 88 + -- Collection 72 89 ----------------------------------------- 73 90 -- # Add 74 91 -- > Add tracks to the collection. ··· 84 101 |> makeParcel 85 102 |> add tracks 86 103 |> resolveParcel model 104 + 105 + ----------------------------------------- 106 + -- Favourites 107 + ----------------------------------------- 108 + ToggleFavouritesOnly -> 109 + Return3.withNothing { model | favouritesOnly = not model.favouritesOnly } 110 + 111 + ----------------------------------------- 112 + -- Search 113 + ----------------------------------------- 114 + ClearSearch -> 115 + Return3.withNothing { model | searchTerm = Nothing } 116 + 117 + Search maybeTerm -> 118 + Return3.withNothing { model | searchTerm = maybeTerm } 119 + 120 + SetSearchTerm term -> 121 + Return3.withNothing { model | searchTerm = Just term } 87 122 88 123 89 124 ··· 126 161 127 162 view : Model -> Html Msg 128 163 view model = 129 - raw 130 - (List.map 131 - (\t -> text t.tags.title) 132 - model.collection.untouched 133 - ) 164 + chunk 165 + [] 166 + [ lazy2 167 + navigation 168 + model.favouritesOnly 169 + model.searchTerm 170 + 171 + -- 172 + , chunk 173 + [] 174 + (List.map 175 + (\t -> text <| t.tags.artist ++ " - " ++ t.tags.title) 176 + model.collection.untouched 177 + ) 178 + ] 179 + 180 + 181 + navigation : Bool -> Maybe String -> Html Msg 182 + navigation favouritesOnly searchTerm = 183 + chunk 184 + [ T.flex ] 185 + [ ----------------------------------------- 186 + -- Part 1 187 + ----------------------------------------- 188 + brick 189 + [ css searchStyles ] 190 + [ T.flex_grow_1 191 + , T.overflow_hidden 192 + , T.relative 193 + ] 194 + [ -- Input 195 + -------- 196 + slab 197 + Html.input 198 + [ css searchInputStyles 199 + , onEnterKey (Search searchTerm) 200 + , onInput SetSearchTerm 201 + , placeholder "Search" 202 + , value (Maybe.withDefault "" searchTerm) 203 + ] 204 + [ T.bg_transparent 205 + , T.bn 206 + , T.flex 207 + , T.h_100 208 + , T.items_center 209 + , T.outline_0 210 + , T.relative 211 + , T.w_100 212 + , T.z_1 213 + ] 214 + [] 215 + 216 + -- Search icon 217 + -------------- 218 + , brick 219 + [ css searchIconStyles ] 220 + [ T.absolute 221 + , T.bottom_0 222 + , T.flex 223 + , T.items_center 224 + , T.left_0 225 + , T.top_0 226 + , T.z_0 227 + ] 228 + [ Html.fromUnstyled (Icons.search searchIconColor 16) ] 229 + 230 + -- Actions 231 + ---------- 232 + , brick 233 + [ css searchActionsStyles ] 234 + [ T.absolute 235 + , T.flex 236 + , T.items_center 237 + , T.right_0 238 + , T.z_2 239 + ] 240 + [ -- 1 241 + case searchTerm of 242 + Just _ -> 243 + brick 244 + [ css searchActionIconStyle 245 + , onClick ClearSearch 246 + , title "Clear search" 247 + ] 248 + [ T.pointer ] 249 + [ Html.fromUnstyled (Icons.clear searchIconColor 16) ] 250 + 251 + Nothing -> 252 + empty 253 + 254 + -- 2 255 + , brick 256 + [ css searchActionIconStyle 257 + , onClick ToggleFavouritesOnly 258 + , title "Toggle favourites-only" 259 + ] 260 + [ T.pointer ] 261 + [ case favouritesOnly of 262 + True -> 263 + Html.fromUnstyled (Icons.favorite UI.Kit.colorKit.base08 16) 264 + 265 + False -> 266 + Html.fromUnstyled (Icons.favorite_border searchIconColor 16) 267 + ] 268 + 269 + -- 3 270 + , empty 271 + ] 272 + ] 273 + , ----------------------------------------- 274 + -- Part 2 275 + ----------------------------------------- 276 + UI.Navigation.local 277 + [ ( Icon Icons.format_list_numbered 278 + , Label "Playlists" Hidden 279 + , PerformMsg Bypass 280 + ) 281 + , ( Icon Icons.event_seat 282 + , Label "Queue" Hidden 283 + , PerformMsg Bypass 284 + ) 285 + , ( Icon Icons.equalizer 286 + , Label "Equalizer" Hidden 287 + , PerformMsg Bypass 288 + ) 289 + ] 290 + ] 291 + 292 + 293 + 294 + -- 🖼 295 + 296 + 297 + searchStyles : List Css.Style 298 + searchStyles = 299 + [ Css.borderBottom3 (Css.px 1) Css.solid (Color.toElmCssColor UI.Kit.colors.subtleBorder) 300 + , Css.borderRight3 (Css.px 1) Css.solid (Color.toElmCssColor UI.Kit.colors.subtleBorder) 301 + ] 302 + 303 + 304 + searchActionsStyles : List Css.Style 305 + searchActionsStyles = 306 + [ Css.marginTop (Css.px 1) 307 + , Css.paddingRight (Css.px <| 13 - 6) 308 + , Css.top (Css.pct 50) 309 + , Css.transform (Css.translateY <| Css.pct -50) 310 + ] 311 + 312 + 313 + searchActionIconStyle : List Css.Style 314 + searchActionIconStyle = 315 + [ Css.marginRight (Css.px 6) ] 316 + 317 + 318 + searchIconColor : Color.Color 319 + searchIconColor = 320 + Color.rgb255 205 205 205 321 + 322 + 323 + searchIconStyles : List Css.Style 324 + searchIconStyles = 325 + [ Css.paddingLeft (Css.px 13) ] 326 + 327 + 328 + searchInputStyles : List Css.Style 329 + searchInputStyles = 330 + [ Css.paddingLeft (Css.px <| 13 + 16 + 9) 331 + , Css.fontSize (Css.px 14) 332 + , Css.height (Css.pct 98) 333 + ]
+22
src/Library/Html/Styled/Ext.elm
··· 1 + module Html.Styled.Ext exposing (ifEnterKey, onEnterKey) 2 + 3 + import Html.Styled exposing (Attribute) 4 + import Html.Styled.Events exposing (keyCode, on) 5 + import Json.Decode as Json 6 + 7 + 8 + {-| Event binding for the `Enter` key. 9 + -} 10 + onEnterKey : msg -> Attribute msg 11 + onEnterKey msg = 12 + on "keydown" (Json.andThen (ifEnterKey msg) keyCode) 13 + 14 + 15 + ifEnterKey : msg -> Int -> Json.Decoder msg 16 + ifEnterKey msg key = 17 + case key of 18 + 13 -> 19 + Json.succeed msg 20 + 21 + _ -> 22 + Json.fail "Another key, that isn't enter, was pressed"