mirror of
https://github.com/heyarne/airsonic-ui.git
synced 2026-05-06 18:33:38 +02:00
Polish and lipstick (#7)
* Add dark sidebar * Add generated covers for items that have none * Fix small spacing issue with generated covers * Set up different sidebar sections and improve styling of bottom bar * Add open-iconic and use icons for playback control buttons * Make sure sidebar always extends to complete height * Simplify album listing view function, add text-overflow to thumbs * Use better identifier for generated covers Makes sure that covers look the same, no matter if generated from an album or individual track * Move shadow-cljs to devDependencies * Display all album titles in a table * Make progress bar take up all available space
This commit is contained in:
parent
a01a419a7d
commit
c8849810db
11 changed files with 236 additions and 52 deletions
18
package-lock.json
generated
18
package-lock.json
generated
|
|
@ -4,6 +4,14 @@
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hugojosefson/color-hash": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hugojosefson/color-hash/-/color-hash-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-ASaDCIwQmyeH6eXdG1Nf2zMOr85Ljp13/8qBSPtYkY1hAr6URRAPG+15i2ogXh/caSolZ4mGfP7MwHPLm/V2Dw==",
|
||||||
|
"requires": {
|
||||||
|
"string-hash": "^1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"abbrev": {
|
"abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
|
@ -4600,6 +4608,11 @@
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"open-iconic": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/open-iconic/-/open-iconic-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-nc/Ix808Yc20ojaxo0eJTJetwMY="
|
||||||
|
},
|
||||||
"optimist": {
|
"optimist": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||||
|
|
@ -5844,6 +5857,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"string-hash": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs="
|
||||||
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
|
|
|
||||||
15
package.json
15
package.json
|
|
@ -7,13 +7,14 @@
|
||||||
"build:cljs": "shadow-cljs release app",
|
"build:cljs": "shadow-cljs release app",
|
||||||
"build:html": "sed 's/\"\\/app\\//\".\\/app\\//g' src/html/index.html > public/index.html",
|
"build:html": "sed 's/\"\\/app\\//\".\\/app\\//g' src/html/index.html > public/index.html",
|
||||||
"build:sass": "node-sass --output-style compressed src/sass/app.sass public/app/style.css",
|
"build:sass": "node-sass --output-style compressed src/sass/app.sass public/app/style.css",
|
||||||
"build": "rm -r public/*; run-p build:*; ",
|
"build": "rm -r public/*; run-p copy:* build:*",
|
||||||
"deploy": "npm run build && gh-pages -d public",
|
"copy:icons": "cp -R node_modules/open-iconic/font/fonts public",
|
||||||
|
"deploy": "npm run build && gh-pages -d public -m \"Deploying $(git rev-parse --short HEAD)\"",
|
||||||
"dev:cljs": "shadow-cljs watch app test",
|
"dev:cljs": "shadow-cljs watch app test",
|
||||||
"dev:html": "sed 's/\"\\.\\/app\\//\"\\/app\\//g' src/html/index.html > public/index.html",
|
"dev:html": "sed 's/\"\\.\\/app\\//\"\\/app\\//g' src/html/index.html > public/index.html",
|
||||||
"dev:sass": "npm run build:sass; node-sass -w src/sass/app.sass public/app/style.css",
|
"dev:sass": "npm run build:sass; node-sass -w src/sass/app.sass public/app/style.css",
|
||||||
"dev:test": "karma start --reporters growl,progress --auto-watch",
|
"dev:test": "karma start --reporters growl,progress --auto-watch",
|
||||||
"dev": "npm-run-all test:compile -p dev:*",
|
"dev": "npm-run-all copy:* test:compile -p dev:*",
|
||||||
"test": "run-s test:compile test:run",
|
"test": "run-s test:compile test:run",
|
||||||
"test:compile": "shadow-cljs compile test",
|
"test:compile": "shadow-cljs compile test",
|
||||||
"test:run": "karma start --single-run"
|
"test:run": "karma start --single-run"
|
||||||
|
|
@ -25,11 +26,12 @@
|
||||||
"url": "git://github.com/heyarne/airsonic-ui.git"
|
"url": "git://github.com/heyarne/airsonic-ui.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hugojosefson/color-hash": "^2.0.3",
|
||||||
"bulma": "^0.7.1",
|
"bulma": "^0.7.1",
|
||||||
"create-react-class": "^15.6.3",
|
"create-react-class": "^15.6.3",
|
||||||
|
"open-iconic": "^1.1.1",
|
||||||
"react": "^16.3.2",
|
"react": "^16.3.2",
|
||||||
"react-dom": "^16.3.2",
|
"react-dom": "^16.3.2"
|
||||||
"shadow-cljs": "^2.3.19"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gh-pages": "^1.1.0",
|
"gh-pages": "^1.1.0",
|
||||||
|
|
@ -41,6 +43,7 @@
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"react-flip-move": "^3.0.1",
|
"react-flip-move": "^3.0.1",
|
||||||
"react-highlight.js": "^1.0.7",
|
"react-highlight.js": "^1.0.7",
|
||||||
"sass": "^1.3.2"
|
"sass": "^1.3.2",
|
||||||
|
"shadow-cljs": "^2.3.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@
|
||||||
|
|
||||||
(defmethod route-data ::main
|
(defmethod route-data ::main
|
||||||
[route-id params query]
|
[route-id params query]
|
||||||
[:api-request "getAlbumList2" :albumList2 {:type "recent"}])
|
[:api-request "getAlbumList2" :albumList2 {:type "recent"
|
||||||
|
:size 18}])
|
||||||
|
|
||||||
(defmethod route-data ::artist-view
|
(defmethod route-data ::artist-view
|
||||||
[route-id params query]
|
[route-id params query]
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(select-keys (:credentials db) [:u :p])))
|
(select-keys (:credentials db) [:u :p])))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
::user
|
||||||
|
(fn [{:keys [credentials]}]
|
||||||
|
{:name (:u credentials)}))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::server
|
::server
|
||||||
(fn [db]
|
(fn [db]
|
||||||
|
|
@ -28,10 +33,19 @@
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::current-content
|
::current-content
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(-> db :response)))
|
(db :response)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
; returns info on the current song as is (basically the metadata you can read from the file system)
|
; returns info on the current song as is (basically the metadata you can read from the file system)
|
||||||
::currently-playing
|
::currently-playing
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(-> db :currently-playing)))
|
(db :currently-playing)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
::is-playing?
|
||||||
|
(fn [query-v _]
|
||||||
|
[(re-frame/subscribe [::currently-playing])])
|
||||||
|
(fn [[currently-playing]]
|
||||||
|
(let [status (:status currently-playing)]
|
||||||
|
(and (not (:paused? status))
|
||||||
|
(not (:ended? status))))))
|
||||||
|
|
|
||||||
|
|
@ -28,21 +28,40 @@
|
||||||
[:h2.title "Recently played"]
|
[:h2.title "Recently played"]
|
||||||
[album/listing (:album content)]])
|
[album/listing (:album content)]])
|
||||||
|
|
||||||
|
(defn sidebar [user]
|
||||||
|
[:aside.menu.section
|
||||||
|
[:p.menu-label "Music"]
|
||||||
|
[:ul.menu-list
|
||||||
|
[:li [:a "By artist"]]
|
||||||
|
[:li [:a "Top rated"]]
|
||||||
|
[:li [:a "Most played"]]]
|
||||||
|
[:p.menu-label "Playlists"]
|
||||||
|
[:p.menu-label "Shares"]
|
||||||
|
[:p.menu-label "Podcasts"]
|
||||||
|
[:p.menu-label "User area"]
|
||||||
|
[:ul.menu-list
|
||||||
|
[:li [:a "Settings"]]
|
||||||
|
;; FIXME: Create proper logout event
|
||||||
|
[:li [:a
|
||||||
|
{:on-click #(dispatch [::events/initialize-db]) :href "#"}
|
||||||
|
(str "Logout (" (:name user) ")")]]]])
|
||||||
|
|
||||||
;; putting everything together
|
;; putting everything together
|
||||||
|
|
||||||
(defn app [route params query]
|
(defn app [route params query]
|
||||||
(let [login @(subscribe [::subs/login])
|
(let [user @(subscribe [::subs/user])
|
||||||
content @(subscribe [::subs/current-content])]
|
content @(subscribe [::subs/current-content])]
|
||||||
[:div
|
[:div
|
||||||
[:section.section>div.container
|
[:main.columns
|
||||||
[:div.level
|
[:div.column.is-2.sidebar
|
||||||
[:div.level-left [:span (str "Currently logged in as " (:u login))]]
|
[sidebar user]]
|
||||||
[:div.level-right [:a {:on-click #(dispatch [::events/initialize-db]) :href "#"} "Logout"]]]
|
[:div.column
|
||||||
[breadcrumbs content]
|
[:section.section
|
||||||
(case route
|
[breadcrumbs content]
|
||||||
::routes/main [most-recent content]
|
(case route
|
||||||
::routes/artist-view [artist-detail content]
|
::routes/main [most-recent content]
|
||||||
::routes/album-view [album-detail content])]
|
::routes/artist-view [artist-detail content]
|
||||||
|
::routes/album-view [album-detail content])]]]
|
||||||
[bottom-bar]]))
|
[bottom-bar]]))
|
||||||
|
|
||||||
(defn main-panel []
|
(defn main-panel []
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
(defn listing [albums]
|
(defn listing [albums]
|
||||||
;; always show 5 in a row
|
;; always show 5 in a row
|
||||||
[:div
|
[:div
|
||||||
(for [albums (partition-all 5 albums)]
|
[:div.columns.is-multiline.is-mobile
|
||||||
[:div.columns
|
(for [[idx album] (map-indexed vector albums)]
|
||||||
(for [[idx album] (map-indexed vector albums)]
|
^{:key idx} [:div.column.is-one-fifth-desktop.is-one-quarter-tablet.is-one-half-mobile
|
||||||
[:div.column {:key idx} [preview album]])])])
|
[preview album]])]])
|
||||||
|
|
|
||||||
|
|
@ -2,40 +2,46 @@
|
||||||
(:require [re-frame.core :refer [dispatch subscribe]]
|
(:require [re-frame.core :refer [dispatch subscribe]]
|
||||||
[airsonic-ui.events :as events]
|
[airsonic-ui.events :as events]
|
||||||
[airsonic-ui.subs :as subs]
|
[airsonic-ui.subs :as subs]
|
||||||
[airsonic-ui.views.cover :refer [cover]]))
|
[airsonic-ui.views.cover :refer [cover]]
|
||||||
|
[airsonic-ui.views.icon :refer [icon]]))
|
||||||
|
|
||||||
;; currently playing / coming next / audio controls...
|
;; currently playing / coming next / audio controls...
|
||||||
|
|
||||||
(defn current-song-info [{:keys [item status]}]
|
(defn current-song-info [{:keys [item status]}]
|
||||||
[:article
|
[:article
|
||||||
[:div (:artist item) " - " (:title item)]
|
[:div (:artist item) " - " (:title item)]
|
||||||
|
;; FIXME: Sometimes items don't have a duration
|
||||||
[:progress.progress.is-tiny {:value (:current-time status)
|
[:progress.progress.is-tiny {:value (:current-time status)
|
||||||
:max (:duration item)}]])
|
:max (:duration item)}]])
|
||||||
|
|
||||||
(defn playback-controls []
|
(defn playback-controls [is-playing?]
|
||||||
|
;; TODO: Toggle play pause icon based on playback status
|
||||||
[:div.field.has-addons
|
[:div.field.has-addons
|
||||||
(let [buttons [["previous" ::events/previous-song]
|
(let [buttons [[:media-step-backward ::events/previous-song]
|
||||||
["play / pause" ::events/toggle-play-pause]
|
[(if is-playing? :media-pause :media-play) ::events/toggle-play-pause]
|
||||||
["next" ::events/next-song]]]
|
[:media-step-forward ::events/next-song]]]
|
||||||
(map (fn [[label event]]
|
(map (fn [[icon-glyph event]]
|
||||||
[:p.control>button.button.is-light {:on-click #(dispatch [event])} label])
|
^{:key icon-glyph} [:p.control>button.button.is-light
|
||||||
|
{:on-click #(dispatch [event])}
|
||||||
|
[icon icon-glyph]])
|
||||||
buttons))])
|
buttons))])
|
||||||
|
|
||||||
(def logo-url "https://airsonic.github.io/airsonic-ui/assets/images/logo/airsonic-dark-350x100.png")
|
(def logo-url "https://airsonic.github.io/airsonic-ui/assets/images/logo/airsonic-light-350x100.png")
|
||||||
|
|
||||||
(defn bottom-bar []
|
(defn bottom-bar []
|
||||||
(let [currently-playing @(subscribe [::subs/currently-playing])]
|
(let [currently-playing @(subscribe [::subs/currently-playing])
|
||||||
[:nav.navbar.is-fixed-bottom
|
is-playing? @(subscribe [::subs/is-playing?])]
|
||||||
|
[:nav.navbar.is-fixed-bottom.playback-area
|
||||||
[:div.navbar-brand
|
[:div.navbar-brand
|
||||||
[:div.navbar-item
|
[:div.navbar-item
|
||||||
[:img {:src logo-url}]]]
|
[:img {:src logo-url}]]]
|
||||||
[:div.navbar-menu.is-active
|
[:div.navbar-menu.is-active
|
||||||
(if currently-playing
|
(if currently-playing
|
||||||
;; show song info
|
;; show song info
|
||||||
[:section.level
|
[:section.level.audio-interaction
|
||||||
[:div.level-left>article.media
|
[:div.level-left>article.media
|
||||||
[:div.media-left [cover (:item currently-playing) 48]]
|
[:div.media-left [cover (:item currently-playing) 48]]
|
||||||
[:div.media-content [current-song-info currently-playing]]]
|
[:div.media-content [current-song-info currently-playing]]]
|
||||||
[:div.level-right [playback-controls]]]
|
[:div.level-right [playback-controls is-playing?]]]
|
||||||
;; not playing anything
|
;; not playing anything
|
||||||
[:span "Currently no song selected"])]]))
|
[:p.idle-notification "Currently no song selected"])]]))
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,69 @@
|
||||||
(ns airsonic-ui.views.cover
|
(ns airsonic-ui.views.cover
|
||||||
(:require [re-frame.core :refer [subscribe]]
|
(:require [clojure.string :as str]
|
||||||
|
[re-frame.core :refer [subscribe]]
|
||||||
|
[reagent.core :as reagent]
|
||||||
[airsonic-ui.subs :as subs]
|
[airsonic-ui.subs :as subs]
|
||||||
[airsonic-ui.utils.api :as api]))
|
[airsonic-ui.utils.api :as api]
|
||||||
|
["@hugojosefson/color-hash" :as ColorHash]))
|
||||||
|
|
||||||
|
(def color-hash (ColorHash.))
|
||||||
|
|
||||||
|
(defn palette
|
||||||
|
"Generate a hsl palette of two colors that's unique for a given item"
|
||||||
|
[item]
|
||||||
|
(let [identifier (str (:artistId item) "-" (or (:albumId item) (:id item)))
|
||||||
|
[h s l] (js->clj (.hsl color-hash identifier))
|
||||||
|
s (str (* 100 s) "%")
|
||||||
|
l (str (* 100 l) "%")]
|
||||||
|
(->>
|
||||||
|
[[h s l]
|
||||||
|
[(mod (+ h (* h 0.3) 10) 360) s l]]
|
||||||
|
(map #(str "hsl(" (str/join "," %) ")")))))
|
||||||
|
|
||||||
;; FIXME: The direct dependency on these subs is a bit ugly
|
;; FIXME: The direct dependency on these subs is a bit ugly
|
||||||
|
|
||||||
|
(defn generate-cover [canvas item]
|
||||||
|
(let [ctx (.getContext canvas "2d")
|
||||||
|
size (.-clientWidth canvas)
|
||||||
|
[a b] (palette item)
|
||||||
|
pad (* 0.02 size)
|
||||||
|
gradient (doto (.createLinearGradient ctx pad 0 (- size pad) size)
|
||||||
|
(.addColorStop 0 a)
|
||||||
|
(.addColorStop 1 b))]
|
||||||
|
(set! (.-fillStyle ctx) gradient)
|
||||||
|
(.fillRect ctx 0 0 size size)))
|
||||||
|
|
||||||
|
(defn missing-cover
|
||||||
|
[item size]
|
||||||
|
(let [dom-node (reagent/atom nil)]
|
||||||
|
(reagent/create-class
|
||||||
|
{:component-did-update
|
||||||
|
(fn [this]
|
||||||
|
(let [canvas @dom-node]
|
||||||
|
(set! (.. canvas -style -width) "100%")
|
||||||
|
(set! (. canvas -width) (.-offsetWidth canvas))
|
||||||
|
(set! (. canvas -height) (.-offsetWidth canvas))
|
||||||
|
(generate-cover canvas item)))
|
||||||
|
|
||||||
|
:component-did-mount
|
||||||
|
(fn [this]
|
||||||
|
(reset! dom-node (reagent/dom-node this)))
|
||||||
|
|
||||||
|
:reagent-render
|
||||||
|
(fn []
|
||||||
|
@dom-node
|
||||||
|
[:canvas.missing-cover])})))
|
||||||
|
|
||||||
|
(defn has-cover? [item]
|
||||||
|
(:coverArt item))
|
||||||
|
|
||||||
(defn cover
|
(defn cover
|
||||||
[item size]
|
[item size]
|
||||||
(let [server @(subscribe [::subs/server])
|
(let [server @(subscribe [::subs/server])
|
||||||
login @(subscribe [::subs/login])
|
login @(subscribe [::subs/login])
|
||||||
url (partial api/cover-url server login item)]
|
url (partial api/cover-url server login item)]
|
||||||
[:figure {:class-name (str "image is-" size "x" size)}
|
[:figure {:class-name (str "image is-" size "x" size)}
|
||||||
[:img {:src (url size)
|
(if (has-cover? item)
|
||||||
:srcset (str (url size) ", " (url (* 2 size)) " 2x")}]]))
|
[:img {:src (url size)
|
||||||
|
:srcSet (str (url size) ", " (url (* 2 size)) " 2x")}]
|
||||||
|
[missing-cover item size])]))
|
||||||
|
|
|
||||||
4
src/cljs/airsonic_ui/views/icon.cljs
Normal file
4
src/cljs/airsonic_ui/views/icon.cljs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
(ns airsonic-ui.views.icon)
|
||||||
|
|
||||||
|
(defn icon [glyph]
|
||||||
|
[:span.icon [:span.oi {:data-glyph (name glyph)}]])
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
(ns airsonic-ui.views.song
|
(ns airsonic-ui.views.song
|
||||||
(:require [re-frame.core :refer [dispatch]]
|
(:require [re-frame.core :refer [dispatch]]
|
||||||
[airsonic-ui.events :as events]
|
[airsonic-ui.events :as events]
|
||||||
[airsonic-ui.routes :as routes :refer [url-for]]))
|
[airsonic-ui.routes :as routes :refer [url-for]]
|
||||||
|
[airsonic-ui.views.icon :refer [icon]]))
|
||||||
|
|
||||||
(defn item [songs song]
|
(defn item [songs song]
|
||||||
(let [artist-id (:artistId song)]
|
(let [artist-id (:artistId song)]
|
||||||
|
|
@ -19,5 +20,10 @@
|
||||||
;; FIXME: This is very similar to album-listing
|
;; FIXME: This is very similar to album-listing
|
||||||
|
|
||||||
(defn listing [songs]
|
(defn listing [songs]
|
||||||
[:ul (for [[idx song] (map-indexed vector songs)]
|
[:table.table.is-striped.is-hoverable.is-fullwidth>tbody
|
||||||
[:li {:key idx} [item songs song]])])
|
(for [[idx song] (map-indexed vector songs)]
|
||||||
|
^{:key idx} [:tr
|
||||||
|
[:td.grow [item songs song]]
|
||||||
|
;; FIXME: Not implemented yet
|
||||||
|
[:td>a {:title "Play next"} [icon :plus]]
|
||||||
|
[:td>a {:title "Play last"} [icon :arrow-thick-right]]])])
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,75 @@
|
||||||
@import "../../node_modules/bulma/bulma"
|
@import "../../node_modules/bulma/bulma"
|
||||||
|
@import "../../node_modules/open-iconic/font/css/open-iconic.scss"
|
||||||
|
|
||||||
|
// area holding content & side navi
|
||||||
|
#app
|
||||||
|
main
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
// navi on the left side
|
||||||
|
.sidebar
|
||||||
|
min-height: 100vh
|
||||||
|
background: $dark
|
||||||
|
a
|
||||||
|
color: $light
|
||||||
|
|
||||||
|
.has-navbar-fixed-bottom .sidebar
|
||||||
|
// 2.5 = 3.25 ($navbar-height) - 0.75 ($padding)
|
||||||
|
min-height: calc(100vh - 2.5rem)
|
||||||
|
|
||||||
|
// bottom bar
|
||||||
|
.playback-area
|
||||||
|
background: $dark
|
||||||
|
color: $light
|
||||||
|
|
||||||
|
.navbar-menu
|
||||||
|
align-items: center
|
||||||
|
|
||||||
|
.audio-interaction
|
||||||
|
flex-grow: 1
|
||||||
|
|
||||||
|
.level-left
|
||||||
|
flex-grow: 1
|
||||||
|
flex-shrink: 0
|
||||||
|
.level-right
|
||||||
|
flex-grow: 0
|
||||||
|
flex-shrink: 1
|
||||||
|
padding-left: .5rem
|
||||||
|
padding-left: .5rem
|
||||||
|
padding-right: .5rem
|
||||||
|
|
||||||
|
.media
|
||||||
|
flex-grow: 1
|
||||||
|
align-items: center
|
||||||
|
|
||||||
|
progress
|
||||||
|
width: 100%
|
||||||
|
|
||||||
.progress.is-tiny
|
.progress.is-tiny
|
||||||
height: 0.25rem
|
height: .25rem
|
||||||
|
|
||||||
|
// cover images
|
||||||
.image.is-256x256
|
.image.is-256x256
|
||||||
// for cover images
|
|
||||||
width: 256px
|
width: 256px
|
||||||
height: 256px
|
height: 256px
|
||||||
|
|
||||||
.album-preview .image.is-256x256
|
.missing-cover
|
||||||
// make sure the grid doesn't overflow
|
display: block
|
||||||
width: auto
|
|
||||||
height: auto
|
.album-preview
|
||||||
max-width: 256px
|
.title,
|
||||||
max-height: 256px
|
.subtitle
|
||||||
|
overflow: hidden
|
||||||
|
white-space: nowrap
|
||||||
|
text-overflow: ellipsis
|
||||||
|
|
||||||
|
.image.is-256x256
|
||||||
|
width: auto
|
||||||
|
height: auto
|
||||||
|
max-width: 256px
|
||||||
|
max-height: 256px
|
||||||
|
|
||||||
|
// occurs in album detail view
|
||||||
|
.table
|
||||||
|
.grow
|
||||||
|
width: 100%
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue