diff --git a/.tool-versions b/.tool-versions index fa0adb2..0688e8b 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -gleam 0.32.2 +gleam 0.32.4 nodejs 20.9.0 diff --git a/normalize.css b/normalize.css index 0bce022..0ebc356 100644 --- a/normalize.css +++ b/normalize.css @@ -287,3 +287,13 @@ img { display: block; max-inline-size: 100%; } + +input[type="range"] { + appearance: none; + width: 100%; /* Specific width is required for Firefox. */ + background: transparent; /* Otherwise white in Chrome */ +} + +input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; +} diff --git a/priv/assets/fonts/Open_Sans/OFL.txt b/priv/assets/fonts/Open_Sans/OFL.txt new file mode 100644 index 0000000..9b448d4 --- /dev/null +++ b/priv/assets/fonts/Open_Sans/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/priv/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf b/priv/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..5bda9cc Binary files /dev/null and b/priv/assets/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf differ diff --git a/priv/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf b/priv/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..e4142bf Binary files /dev/null and b/priv/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf differ diff --git a/priv/assets/fonts/Open_Sans/README.txt b/priv/assets/fonts/Open_Sans/README.txt new file mode 100644 index 0000000..2548322 --- /dev/null +++ b/priv/assets/fonts/Open_Sans/README.txt @@ -0,0 +1,100 @@ +Open Sans Variable Font +======================= + +This download contains Open Sans as both variable fonts and static fonts. + +Open Sans is a variable font with these axes: + wdth + wght + +This means all the styles are contained in these files: + OpenSans-VariableFont_wdth,wght.ttf + OpenSans-Italic-VariableFont_wdth,wght.ttf + +If your app fully supports variable fonts, you can now pick intermediate styles +that aren’t available as static fonts. Not all apps support variable fonts, and +in those cases you can use the static font files for Open Sans: + static/OpenSans_Condensed-Light.ttf + static/OpenSans_Condensed-Regular.ttf + static/OpenSans_Condensed-Medium.ttf + static/OpenSans_Condensed-SemiBold.ttf + static/OpenSans_Condensed-Bold.ttf + static/OpenSans_Condensed-ExtraBold.ttf + static/OpenSans_SemiCondensed-Light.ttf + static/OpenSans_SemiCondensed-Regular.ttf + static/OpenSans_SemiCondensed-Medium.ttf + static/OpenSans_SemiCondensed-SemiBold.ttf + static/OpenSans_SemiCondensed-Bold.ttf + static/OpenSans_SemiCondensed-ExtraBold.ttf + static/OpenSans-Light.ttf + static/OpenSans-Regular.ttf + static/OpenSans-Medium.ttf + static/OpenSans-SemiBold.ttf + static/OpenSans-Bold.ttf + static/OpenSans-ExtraBold.ttf + static/OpenSans_Condensed-LightItalic.ttf + static/OpenSans_Condensed-Italic.ttf + static/OpenSans_Condensed-MediumItalic.ttf + static/OpenSans_Condensed-SemiBoldItalic.ttf + static/OpenSans_Condensed-BoldItalic.ttf + static/OpenSans_Condensed-ExtraBoldItalic.ttf + static/OpenSans_SemiCondensed-LightItalic.ttf + static/OpenSans_SemiCondensed-Italic.ttf + static/OpenSans_SemiCondensed-MediumItalic.ttf + static/OpenSans_SemiCondensed-SemiBoldItalic.ttf + static/OpenSans_SemiCondensed-BoldItalic.ttf + static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf + static/OpenSans-LightItalic.ttf + static/OpenSans-Italic.ttf + static/OpenSans-MediumItalic.ttf + static/OpenSans-SemiBoldItalic.ttf + static/OpenSans-BoldItalic.ttf + static/OpenSans-ExtraBoldItalic.ttf + +Get started +----------- + +1. Install the font files you want to use + +2. Use your app's font picker to view the font family and all the +available styles + +Learn more about variable fonts +------------------------------- + + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts + https://variablefonts.typenetwork.com + https://medium.com/variable-fonts + +In desktop apps + + https://theblog.adobe.com/can-variable-fonts-illustrator-cc + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts + +Online + + https://developers.google.com/fonts/docs/getting_started + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts + +Installing fonts + + MacOS: https://support.apple.com/en-us/HT201749 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows + +Android Apps + + https://developers.google.com/fonts/docs/android + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts + +License +------- +Please read the full license text (OFL.txt) to understand the permissions, +restrictions and requirements for usage, redistribution, and modification. + +You can use them in your products & projects – print or digital, +commercial or otherwise. + +This isn't legal advice, please consider consulting a lawyer and see the full +license for all details. diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf new file mode 100644 index 0000000..4a5bc39 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-Bold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf new file mode 100644 index 0000000..8878a3e Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-BoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf new file mode 100644 index 0000000..5dfb66c Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-ExtraBold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf new file mode 100644 index 0000000..d266998 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-ExtraBoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf new file mode 100644 index 0000000..e84f9ee Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-Italic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-Light.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-Light.ttf new file mode 100644 index 0000000..cf8e0c7 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-Light.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf new file mode 100644 index 0000000..d913f35 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-LightItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf new file mode 100644 index 0000000..a76d4ce Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-Medium.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf new file mode 100644 index 0000000..5599691 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-MediumItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf new file mode 100644 index 0000000..29e9e60 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-Regular.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf new file mode 100644 index 0000000..a571167 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-SemiBold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf new file mode 100644 index 0000000..a7d2323 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans-SemiBoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf new file mode 100644 index 0000000..90d25e5 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Bold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf new file mode 100644 index 0000000..9fefa96 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-BoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf new file mode 100644 index 0000000..ec9e308 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf new file mode 100644 index 0000000..f4a2648 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf new file mode 100644 index 0000000..451059e Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Italic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf new file mode 100644 index 0000000..9823525 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Light.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf new file mode 100644 index 0000000..d1f9808 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-LightItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf new file mode 100644 index 0000000..50a9f70 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Medium.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf new file mode 100644 index 0000000..e24fdca Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-MediumItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf new file mode 100644 index 0000000..3aa5d46 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-Regular.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf new file mode 100644 index 0000000..1b98bc4 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf new file mode 100644 index 0000000..318828d Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_Condensed-SemiBoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf new file mode 100644 index 0000000..dc2168f Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Bold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf new file mode 100644 index 0000000..36818ec Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-BoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf new file mode 100644 index 0000000..64b8c41 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf new file mode 100644 index 0000000..09f3851 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf new file mode 100644 index 0000000..690ce39 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Italic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf new file mode 100644 index 0000000..443bc12 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Light.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf new file mode 100644 index 0000000..b804514 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-LightItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf new file mode 100644 index 0000000..8c143cd Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Medium.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf new file mode 100644 index 0000000..d4564c9 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-MediumItalic.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf new file mode 100644 index 0000000..8130446 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-Regular.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf new file mode 100644 index 0000000..99b6069 Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBold.ttf differ diff --git a/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf new file mode 100644 index 0000000..c89a1cf Binary files /dev/null and b/priv/assets/fonts/Open_Sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf differ diff --git a/src/elekf/web/authed_view.gleam b/src/elekf/web/authed_view.gleam index 49a6114..64880ec 100644 --- a/src/elekf/web/authed_view.gleam +++ b/src/elekf/web/authed_view.gleam @@ -6,7 +6,7 @@ import gleam/list import gleam/option import gleam/javascript/promise import lustre/element.{text} -import lustre/element/html.{button, div, nav, p} +import lustre/element/html.{div, nav, p} import lustre/attribute import lustre/effect import lustre/event @@ -26,6 +26,8 @@ import elekf/web/components/library_views/tracks_view import elekf/web/components/library_views/artists_view import elekf/web/components/library_views/albums_view import elekf/web/components/library_views/single_artist_view +import elekf/web/components/button_group +import elekf/web/components/button import elekf/web/events/start_play import elekf/web/events/show_artist import elekf/web/utils @@ -202,26 +204,26 @@ pub fn view(model: Model) { nav( [attribute.id("library-top-nav")], [ - button( + button_group.view( + "", + [], [ - attribute.type_("button"), - event.on_click(ChangeView(library_view.Tracks)), + button.view( + "", + [event.on_click(ChangeView(library_view.Tracks))], + [icon("music-note-beamed", Alt("Tracks"))], + ), + button.view( + "", + [event.on_click(ChangeView(library_view.Artists))], + [icon("file-person", Alt("Artists"))], + ), + button.view( + "", + [event.on_click(ChangeView(library_view.Albums))], + [icon("disc", Alt("Albums"))], + ), ], - [icon("music-note-beamed", Alt("Tracks"))], - ), - button( - [ - attribute.type_("button"), - event.on_click(ChangeView(library_view.Artists)), - ], - [icon("file-person", Alt("Artists"))], - ), - button( - [ - attribute.type_("button"), - event.on_click(ChangeView(library_view.Albums)), - ], - [icon("disc", Alt("Albums"))], ), ], ), @@ -230,32 +232,58 @@ pub fn view(model: Model) { tracks_view.render( model.library, model.settings, - [attribute.id("tracks-view"), start_play.on(StartPlay)], + [ + attribute.id("tracks-view"), + attribute.class("glass-bg"), + start_play.on(StartPlay), + ], ) library_view.Artists -> artists_view.render( model.library, model.settings, - [attribute.id("artists-view"), show_artist.on(ShowArtist)], + [ + attribute.id("artists-view"), + attribute.class("glass-bg"), + show_artist.on(ShowArtist), + ], ) library_view.Albums -> albums_view.render( model.library, model.settings, - [attribute.id("albums-view"), start_play.on(StartPlay)], + [ + attribute.id("albums-view"), + attribute.class("glass-bg"), + start_play.on(StartPlay), + ], ) library_view.SingleArtist(artist_info) -> single_artist_view.render( model.library, artist_info, model.settings, - [attribute.id("single-artist-view"), start_play.on(StartPlay)], + [ + attribute.id("single-artist-view"), + attribute.class("glass-bg"), + start_play.on(StartPlay), + ], ) }, ], ), div( - [attribute.id("authed-view-player")], + [ + attribute.id("authed-view-player"), + attribute.class( + "glass-bg glass-shadow glass-blur glass-border " <> case + model.play_status + { + HasTracks(_) -> "" + NoTracks -> "hidden" + }, + ), + ], [ case model.play_status { HasTracks(PlayInfo(player: player, ..)) -> diff --git a/src/elekf/web/components/button.gleam b/src/elekf/web/components/button.gleam new file mode 100644 index 0000000..2dba1d0 --- /dev/null +++ b/src/elekf/web/components/button.gleam @@ -0,0 +1,18 @@ +import lustre/attribute +import lustre/element +import lustre/element/html.{button} + +pub fn view( + class: String, + extra_attributes: List(attribute.Attribute(a)), + content: List(element.Element(a)), +) { + button( + [ + attribute.class("glass-button " <> class), + attribute.type_("button"), + ..extra_attributes + ], + content, + ) +} diff --git a/src/elekf/web/components/button_group.gleam b/src/elekf/web/components/button_group.gleam new file mode 100644 index 0000000..eb1b201 --- /dev/null +++ b/src/elekf/web/components/button_group.gleam @@ -0,0 +1,11 @@ +import lustre/attribute +import lustre/element +import lustre/element/html.{div} + +pub fn view( + class: String, + extra_attributes: List(attribute.Attribute(a)), + content: List(element.Element(a)), +) { + div([attribute.class("button-group " <> class), ..extra_attributes], content) +} diff --git a/src/elekf/web/components/library_item.gleam b/src/elekf/web/components/library_item.gleam index 2de4254..6d308cb 100644 --- a/src/elekf/web/components/library_item.gleam +++ b/src/elekf/web/components/library_item.gleam @@ -1,3 +1,15 @@ +import lustre/attribute +import lustre/element +import lustre/element/html.{div} + /// An item in the library with its ID. pub type LibraryItem(a) = #(Int, a) + +pub fn view( + class: String, + extra_attributes: List(attribute.Attribute(a)), + content: List(element.Element(a)), +) { + div([attribute.class("library-item " <> class), ..extra_attributes], content) +} diff --git a/src/elekf/web/components/library_view.gleam b/src/elekf/web/components/library_view.gleam index 3bf0d1f..9eaee1b 100644 --- a/src/elekf/web/components/library_view.gleam +++ b/src/elekf/web/components/library_view.gleam @@ -21,6 +21,7 @@ import elekf/web/events/start_play import elekf/web/events/show_artist import elekf/web/components/search import elekf/web/components/library_item.{type LibraryItem} +import elekf/web/components/shuffle_all import elekf/web/common /// Function to get the data of the view from the library. @@ -126,13 +127,11 @@ pub fn render( ) { element.element( name, - list.flatten([ - [ - attribute.property("library", library), - attribute.property("settings", settings), - ], - extra_attrs, - ]), + [ + attribute.property("library", library), + attribute.property("settings", settings), + ..extra_attrs + ], [], ) } @@ -199,13 +198,7 @@ pub fn library_view( [ search.view(model.search) |> element.map(Search), - div( - [ - attribute.class("library-item library-list-shuffle-all"), - event.on_click(ShuffleAll), - ], - [h3([attribute.class("library-item-title")], [text("Shuffle all")])], - ), + shuffle_all.view([event.on_click(ShuffleAll)]), ], list.index_map(items, fn(i, item) { item_view(model, items, i, item) }) |> list.flatten(), diff --git a/src/elekf/web/components/library_views/album_item.gleam b/src/elekf/web/components/library_views/album_item.gleam index 4eb06f5..ce3e417 100644 --- a/src/elekf/web/components/library_views/album_item.gleam +++ b/src/elekf/web/components/library_views/album_item.gleam @@ -12,10 +12,11 @@ import elekf/library/album.{type Album} import elekf/library/track.{type Track} import elekf/library/album_utils import elekf/library/track_utils -import elekf/web/components/library_view.{AlbumExpandToggled} +import elekf/web/components/library_view.{AlbumExpandToggled, ShuffleAll} import elekf/web/components/library_item.{type LibraryItem} import elekf/web/components/library_views/track_item import elekf/web/components/library_views/thumbnail +import elekf/web/components/shuffle_all import elekf/web/common.{type Settings} const base_classes = "library-item album-item" @@ -105,7 +106,7 @@ pub fn view_tracks(library: Library, tracks: List(LibraryItem(Track))) { ), ], list.flatten([ - [h3([attribute.class("library-item-title")], [text("Shuffle all")])], + [shuffle_all.view([event.on_click(ShuffleAll)])], list.index_map( tracks, fn(index, track_item) { diff --git a/src/elekf/web/components/library_views/single_artist_view.gleam b/src/elekf/web/components/library_views/single_artist_view.gleam index 22403b8..341aad8 100644 --- a/src/elekf/web/components/library_views/single_artist_view.gleam +++ b/src/elekf/web/components/library_views/single_artist_view.gleam @@ -68,10 +68,7 @@ pub fn render( component_name, library, settings, - list.flatten([ - [attribute.property("artist", option.Some(artist))], - extra_attrs, - ]), + [attribute.property("artist", option.Some(artist)), ..extra_attrs], ) } diff --git a/src/elekf/web/components/library_views/track_item.gleam b/src/elekf/web/components/library_views/track_item.gleam index 8797c2f..440786f 100644 --- a/src/elekf/web/components/library_views/track_item.gleam +++ b/src/elekf/web/components/library_views/track_item.gleam @@ -2,7 +2,7 @@ import gleam/int import lustre/attribute import lustre/event import lustre/element.{text} -import lustre/element/html.{div, h3, p} +import lustre/element/html.{h3, p} import elekf/library.{type Library} import elekf/library/track.{type Track} import elekf/web/components/library_item.{type LibraryItem} @@ -19,10 +19,10 @@ pub fn view( let album = library.assert_album(library, track.album_id) let artist = library.assert_artist(library, track.artist_id) [ - div( + library_item.view( + "track-item", [ attribute.id(id_prefix <> "-" <> int.to_string(track_id)), - attribute.class("library-item track-item"), attribute.type_("button"), event.on_click(StartPlay(items, index)), attribute.attribute("role", "button"), diff --git a/src/elekf/web/components/player.gleam b/src/elekf/web/components/player.gleam index 8b61aa1..0326e79 100644 --- a/src/elekf/web/components/player.gleam +++ b/src/elekf/web/components/player.gleam @@ -4,7 +4,7 @@ import gleam/uri import gleam/int import gleam/float import lustre/element.{text} -import lustre/element/html.{audio, button, div, input, p} +import lustre/element/html.{audio, div, input, p} import lustre/attribute import lustre/event import lustre/effect @@ -12,6 +12,8 @@ import elektrofoni import elekf/web/common import elekf/web/components/icon.{Alt, icon} import elekf/web/components/track_length.{track_length} +import elekf/web/components/button_group +import elekf/web/components/button import elekf/library/track.{type Track} import elekf/utils/date import ibroadcast/authed_request.{type RequestConfig} @@ -150,41 +152,35 @@ pub fn view(model: Model) { div( [attribute.id("player-controls")], [ - button( + button_group.view( + "", + [], [ - attribute.id("player-previous"), - attribute.type_("button"), - event.on_click(PrevTrack), + button.view( + "button-small", + [attribute.id("player-previous"), event.on_click(PrevTrack)], + [icon("skip-backward-fill", Alt("Previous"))], + ), + case is_playing { + True -> + button.view( + "button-small", + [attribute.id("player-play-pause"), event.on_click(Pause)], + [icon("pause-fill", Alt("Pause"))], + ) + False -> + button.view( + "button-small", + [attribute.id("player-play-pause"), event.on_click(Play)], + [icon("play-fill", Alt("Play"))], + ) + }, + button.view( + "button-small", + [attribute.id("player-next"), event.on_click(NextTrack)], + [icon("skip-forward-fill", Alt("Next"))], + ), ], - [icon("skip-backward-fill", Alt("Previous"))], - ), - case is_playing { - True -> - button( - [ - attribute.id("player-play-pause"), - attribute.type_("button"), - event.on_click(Pause), - ], - [icon("pause-fill", Alt("Pause"))], - ) - False -> - button( - [ - attribute.id("player-play-pause"), - attribute.type_("button"), - event.on_click(Play), - ], - [icon("play-fill", Alt("Play"))], - ) - }, - button( - [ - attribute.id("player-next"), - attribute.type_("button"), - event.on_click(NextTrack), - ], - [icon("skip-forward-fill", Alt("Next"))], ), input([ attribute.id("player-track"), diff --git a/src/elekf/web/components/search.gleam b/src/elekf/web/components/search.gleam index 7a722a6..5be0edb 100644 --- a/src/elekf/web/components/search.gleam +++ b/src/elekf/web/components/search.gleam @@ -1,10 +1,11 @@ //// The search view renders a search bar, other views must do the searching //// based on the emitted messages. -import lustre/element/html.{button, div, input} +import lustre/element/html.{div, input} import lustre/attribute import lustre/event import elekf/web/components/icon.{Alt, icon} +import elekf/web/components/button import elekf/utils/lustre const search_bar_input_id = "search-bar-input" @@ -57,9 +58,9 @@ pub fn view(model: Model) { ]), ], ), - button( + button.view( + "button-small", [ - attribute.type_("button"), attribute.attribute("aria-controls", search_bar_input_id), event.on_click(ToggleShow), ], diff --git a/src/elekf/web/components/shuffle_all.gleam b/src/elekf/web/components/shuffle_all.gleam new file mode 100644 index 0000000..f2aa678 --- /dev/null +++ b/src/elekf/web/components/shuffle_all.gleam @@ -0,0 +1,16 @@ +import lustre/element/html.{div, h3} +import lustre/element.{text} +import lustre/attribute +import elekf/web/components/icon + +pub fn view(extra_attrs: List(attribute.Attribute(a))) { + div( + [attribute.class("library-item library-list-shuffle-all")], + [ + h3( + [attribute.class("library-item-title"), ..extra_attrs], + [icon.icon("shuffle", icon.Hidden), text(" Shuffle all")], + ), + ], + ) +} diff --git a/style.css b/style.css index a0335e3..92b0078 100644 --- a/style.css +++ b/style.css @@ -1,28 +1,132 @@ +@font-face { + font-family: "Open Sans"; + src: url("./priv/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth\,wght.ttf") + format("truetype") tech("variations"); + src: url("./priv/assets/fonts/Open_Sans/OpenSans-VariableFont_wdth\,wght.ttf") + format("truetype-variations"); + font-weight: 300 800; + font-stretch: 75% 100%; +} + :root { - --background-color: #abcdef; + --font-family: "Open Sans", ui-sans-serif; + + --background-gradient-bottom: rgb(131, 58, 180, 1); + --background-gradient-middle: rgba(253, 29, 29, 1); + --background-gradient-top: rgba(252, 176, 69, 1); --text-color: #123456; + + --glass-opacity: 0.3; + --glass-shadow-opacity: 0.1; + --glass-background: rgba(255, 255, 255, var(--glass-opacity)); + --glass-blur: 10px; + --glass-border-color: rgba(255, 255, 255, var(--glass-opacity)); + --glass-border: 1px solid var(--glass-border-color); + --glass-shadow: 0 4px 30px rgba(0, 0, 0, var(--glass-shadow-opacity)); + + --button-border-radius-small: 5px; + --button-border-radius: 10px; + + --side-margin: 5px; + + --track-thumb-size: 20px; + --track-scale-factor: 4; + + --library-top-nav-height: 45px; +} + +.glass-bg, +.glass-button { + background: var(--glass-background); +} + +.glass-shadow, +.glass-button { + box-shadow: var(--glass-shadow); +} + +.glass-blur, +.glass-button { + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); +} + +.glass-border, +.glass-button { + border: var(--glass-border); +} + +.glass-button { + border-radius: var(--button-border-radius); +} + +.button-small { + border-radius: var(--button-border-radius-small); +} + +.button-group { + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: stretch; +} + +.button-group .glass-button { + border-radius: 0; + border-right: none; +} + +.button-group .glass-button:first-child { + border-radius: var(--button-border-radius) 0 0 var(--button-border-radius); +} + +.button-group .glass-button:last-child { + border-radius: 0 var(--button-border-radius) var(--button-border-radius) 0; + border-right: var(--glass-border); +} + +.hidden { + display: none; +} + +html { + font-family: var(--font-family); } body { - background-color: var(--background-color); + background-color: var(--background-gradient-bottom); + background: linear-gradient( + 0deg, + var(--background-gradient-bottom) 0%, + var(--background-gradient-middle) 50%, + var(--background-gradient-top) 100% + ); color: var(--text-color); - overflow: hidden; + overflow-y: hidden; } main { - margin: 10px; - overflow: hidden; + margin: var(--side-margin); + overflow-y: hidden; +} + +tracks-view, +albums-view, +artists-view, +single-artist-view { + display: block; + + margin-top: calc(var(--library-top-nav-height) + var(--side-margin) * 2); + overflow-y: hidden; + height: calc( + 100vh - (var(--library-top-nav-height) + var(--side-margin) * 2) - + var(--side-margin) * 2 + ); } #authed-view-wrapper { - height: calc(100vh - 2 * 10px); - overflow: hidden; - - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: stretch; - gap: 10px; + height: calc(100vh - 2 * var(--side-margin)); + overflow-y: hidden; } #authed-view-library { @@ -30,11 +134,96 @@ main { overflow-y: auto; } -#library-top-nav { +#authed-view-player { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + padding: var(--side-margin); + + border-radius: 10px 10px 0 0; + border-bottom: none; +} + +#player-wrapper { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 5px; +} + +#player-wrapper-track-title { + font-weight: 100; + white-space: nowrap; + text-overflow: ellipsis; + overflow-x: hidden; +} + +#player-controls { display: flex; flex-direction: row; - justify-content: space-around; - align-items: stretch; + justify-content: space-between; + gap: 5px; +} + +#player-track { + flex: 1 1; +} + +#player-track::-webkit-slider-thumb { + appearance: none; + border: var(--glass-border); + height: var(--track-thumb-size); + width: var(--track-thumb-size); + border-radius: 50%; + background: var(--glass-background); + cursor: pointer; + box-shadow: var(--glass-shadow); + transform: translateY( + calc( + -50% - 0.5px + var(--track-thumb-size) / (var(--track-scale-factor) * 2) + ) + ); +} +#player-track::-moz-range-thumb { + appearance: none; + border: var(--glass-border); + height: var(--track-thumb-size); + width: var(--track-thumb-size); + border-radius: 50%; + background: var(--glass-background); + cursor: pointer; + box-shadow: var(--glass-shadow); +} + +#player-track::-webkit-slider-runnable-track { + width: calc(100% - var(--track-thumb-size)); + height: calc(var(--track-thumb-size) / var(--track-scale-factor)); + cursor: pointer; + box-shadow: var(--glass-shadow); + background: var(--glass-background); + border-radius: 1.3px; + border: 0.2px solid var(--glass-border-color); +} +#player-track::-moz-range-track { + width: calc(100% - var(--track-thumb-size)); + height: calc(var(--track-thumb-size) / var(--track-scale-factor)); + cursor: pointer; + box-shadow: var(--glass-shadow); + background: var(--glass-background); + border-radius: 1.3px; + border: 0.2px solid var(--glass-border-color); +} + +#library-top-nav { + position: fixed; + width: 100%; + height: var(--library-top-nav-height); +} + +#library-top-nav .button-group { + width: 100%; + height: 100%; } #library-top-nav button { @@ -65,6 +254,11 @@ main { width: 100%; } +.library-list { + height: 100%; + overflow-y: auto; +} + #artists-view .library-list, #albums-view .library-list, #single-artist-view .library-list { @@ -74,8 +268,14 @@ main { gap: 10px; } +.track-item { + padding-left: var(--side-margin); + padding-right: var(--side-margin); +} + .library-list-shuffle-all { grid-column: 1 / -1; + padding: calc(var(--side-margin) * 2) var(--side-margin); } .library-item-thumbnail {