diff --git a/src/Applications/UI.elm b/src/Applications/UI.elm index 64d7f22f..66154034 100644 --- a/src/Applications/UI.elm +++ b/src/Applications/UI.elm @@ -135,8 +135,8 @@ init flags url key = ----------------------------------------- -- Equalizer ----------------------------------------- - , eqKnobOperation = Nothing , eqSettings = Equalizer.defaultSettings + , showVolumeSlider = False ----------------------------------------- -- Instances @@ -306,17 +306,11 @@ update msg = ----------------------------------------- -- Equalizer ----------------------------------------- - ActivateKnob a b -> - Equalizer.activateKnob a b + AdjustVolume a -> + Equalizer.adjustVolume a - AdjustKnob a -> - Equalizer.adjustKnob a - - DeactivateKnob _ -> - Equalizer.deactivateKnob - - ResetKnob a -> - Equalizer.resetKnob a + ToggleVolumeSlider a -> + Equalizer.toggleVolumeSlider a ----------------------------------------- -- Interface diff --git a/src/Applications/UI/Adjunct.elm b/src/Applications/UI/Adjunct.elm index c3d7e88b..692652d7 100644 --- a/src/Applications/UI/Adjunct.elm +++ b/src/Applications/UI/Adjunct.elm @@ -108,9 +108,6 @@ keyboardInput msg model = [ Keyboard.Character "3", Keyboard.Control ] -> Common.changeUrlUsingPage (Page.Queue Queue.Index) m - [ Keyboard.Character "4", Keyboard.Control ] -> - Common.changeUrlUsingPage Page.Equalizer m - [ Keyboard.Character "8", Keyboard.Control ] -> Common.changeUrlUsingPage (Page.Sources Sources.Index) m @@ -133,9 +130,6 @@ keyboardInput msg model = else case m.page of - Page.Equalizer -> - Return.singleton { m | page = Page.Index } - Page.Playlists Playlists.Index -> Return.singleton { m | page = Page.Index } diff --git a/src/Applications/UI/Equalizer/State.elm b/src/Applications/UI/Equalizer/State.elm index ad18ab5f..d5425338 100644 --- a/src/Applications/UI/Equalizer/State.elm +++ b/src/Applications/UI/Equalizer/State.elm @@ -1,106 +1,35 @@ module UI.Equalizer.State exposing (..) -import Coordinates +import Common exposing (Switch(..)) import Equalizer exposing (..) -import Html.Events.Extra.Pointer as Pointer -import Return exposing (andThen, return) +import Return exposing (return) import UI.Ports as Ports import UI.Types exposing (..) -import UI.User.State.Export as User -- 📣 -activateKnob : Knob -> Pointer.Event -> Manager -activateKnob theKnob { pointer } model = - { knob = theKnob - , startingPosition = Coordinates.fromTuple pointer.clientPos - } - |> (\m -> { model | eqKnobOperation = Just m }) - |> Return.singleton - - -adjustKnob : Pointer.Event -> Manager -adjustKnob { pointer } model = +adjustVolume : Float -> Manager +adjustVolume volume model = let - start = - case model.eqKnobOperation of - Just { startingPosition } -> - startingPosition - - Nothing -> - { x = 0, y = 0 } - - end = - (\( a, b ) -> { x = a, y = b }) - pointer.clientPos - - x = - end.x - start.x - - y = - start.y - end.y - - distance = - sqrt (x ^ 2 + y ^ 2) - - angle = - atan2 x y - * (180 / pi) - |> max (maxAngle * -1) - |> min maxAngle - - value = - case ( distance > 10, Maybe.map .knob model.eqKnobOperation ) of - ( True, Just Volume ) -> - Just ( Volume, (maxAngle + angle) / (maxAngle * 2) ) - - ( True, Just knobType ) -> - Just ( knobType, angle / maxAngle ) - - _ -> - Nothing - settings = model.eqSettings - - newSettings = - case value of - Just ( Low, v ) -> - { settings | low = v } - - Just ( Mid, v ) -> - { settings | mid = v } - - Just ( High, v ) -> - { settings | high = v } - - Just ( Volume, v ) -> - { settings | volume = v } - - Nothing -> - settings in - case value of - Just ( knobType, v ) -> - return { model | eqSettings = newSettings } (adjustKnobUsingPort knobType v) + return + { model | eqSettings = { settings | volume = volume } } + (adjustKnobUsingPort Volume volume) - Nothing -> - Return.singleton { model | eqSettings = newSettings } +toggleVolumeSlider : Switch -> Manager +toggleVolumeSlider switch model = + case switch of + On -> + Return.singleton { model | showVolumeSlider = True } -deactivateKnob : Manager -deactivateKnob model = - User.saveEnclosedUserData { model | eqKnobOperation = Nothing } - - -resetKnob : Knob -> Manager -resetKnob knob model = - model - |> resetKnobOrganizer knob - |> andThen User.saveEnclosedUserData + Off -> + Return.singleton { model | showVolumeSlider = False } @@ -129,43 +58,4 @@ adjustKnobUsingPort knobType value = adjustAllKnobs : Settings -> Cmd Msg adjustAllKnobs eqSettings = - Cmd.batch - [ adjustKnobUsingPort Low eqSettings.low - , adjustKnobUsingPort Mid eqSettings.mid - , adjustKnobUsingPort High eqSettings.high - , adjustKnobUsingPort Volume eqSettings.volume - ] - - - --- ㊙️ - - -resetKnobOrganizer : Knob -> Manager -resetKnobOrganizer knob model = - let - d = - defaultSettings - - s = - model.eqSettings - in - case knob of - Low -> - reset Low { s | low = d.low } d.low model - - Mid -> - reset Mid { s | mid = d.mid } d.mid model - - High -> - reset High { s | high = d.high } d.high model - - Volume -> - reset Volume { s | volume = d.volume } d.volume model - - -reset : Knob -> Equalizer.Settings -> Float -> Manager -reset knobType newSettings value model = - ( { model | eqSettings = newSettings } - , adjustKnobUsingPort knobType value - ) + adjustKnobUsingPort Volume eqSettings.volume diff --git a/src/Applications/UI/Equalizer/View.elm b/src/Applications/UI/Equalizer/View.elm deleted file mode 100644 index 0ed208ef..00000000 --- a/src/Applications/UI/Equalizer/View.elm +++ /dev/null @@ -1,230 +0,0 @@ -module UI.Equalizer.View exposing (view) - -import Chunky exposing (..) -import Common -import Equalizer exposing (..) -import Html exposing (Html) -import Html.Attributes as A -import Html.Events -import Html.Events.Extra.Pointer as Pointer -import Json.Decode as Decode -import Material.Icons.Round as Icons -import Svg -import Svg.Attributes -import UI.Kit -import UI.Navigation exposing (..) -import UI.Page -import UI.Types exposing (Msg(..)) - - - --- 🗺 - - -view : Settings -> Html Msg -view settings = - UI.Kit.receptacle - { scrolling = True } - [ ----------------------------------------- - -- Navigation - ----------------------------------------- - UI.Navigation.local - [ ( Icon Icons.arrow_back - , Label Common.backToIndex Hidden - , NavigateToPage UI.Page.Index - ) - ] - - ----------------------------------------- - -- Content - ----------------------------------------- - , chunk - [ "relative", "select-none" ] - [ chunk - [ "absolute", "left-0", "top-0" ] - [ UI.Kit.canister [ UI.Kit.h1 "Volume" ] - ] - ] - - -- - , UI.Kit.centeredContent - [ eqView settings ] - ] - - -eqView : Settings -> Html Msg -eqView settings = - chunk - [ "equalizer" - - -- - , "text-center" - ] - [ chunk - [ "border" - , "border-black-05" - , "rounded" - , "flex" - - -- Dark mode - ------------ - , "dark:border-base00" - ] - [ knob Volume settings.volume - ] - ] - - - --- KNOB - - -knob : Knob -> Float -> Html Msg -knob knobType value = - chunk - [ "border-black-05" - , "border-r" - , "flex-grow" - , "flex-shrink-0" - , "px-10" - , "py-4" - - -- - , "last:border-r-0" - - -- - , "sm:px-12" - , "md:px-16" - ] - [ knob_ knobType value - , knobLines - , knobLabel knobType - ] - - -knob_ : Knob -> Float -> Html Msg -knob_ knobType value = - let - angle = - case knobType of - Volume -> - (value * maxAngle * 2) - maxAngle - - _ -> - value * maxAngle - - resetDecoder = - Decode.succeed - { message = ResetKnob knobType - , stopPropagation = True - , preventDefault = True - } - in - brick - [ A.style "transform" ("rotate(" ++ String.fromFloat angle ++ "deg)") - - -- - , Html.Events.custom "dblclick" resetDecoder - , Html.Events.custom "dbltap" resetDecoder - - -- - , knobType - |> ActivateKnob - |> Pointer.onDown - ] - [ "knob" - - -- - , "cursor-pointer" - , "mx-auto" - , "overflow-hidden" - , "relative" - , "rounded-full" - ] - [ Html.map never knob__ ] - - -knob__ : Html Never -knob__ = - Html.div - [] - [ decagonSvg - , chunk - [ "layer-a" - - -- - , "absolute" - , "inset-0" - , "rounded-full" - , "z-10" - ] - [ chunk - [ "layer-b" - - -- - , "mx-auto" - ] - [] - ] - ] - - -knobLabel : Knob -> Html Msg -knobLabel knobType = - chunk - [ "knob-label" - - -- - , "font-semibold" - , "mt-3" - , "opacity-70" - , "tracking-wide" - ] - [ case knobType of - Low -> - Html.text "LOW" - - Mid -> - Html.text "MID" - - High -> - Html.text "HIGH" - - Volume -> - Html.text "VOLUME" - ] - - -knobLines : Html Msg -knobLines = - chunk - [ "knob-lines" - - -- - , "mx-auto" - , "relative" - ] - [ chunk [ "absolute", "left-0", "top-0" ] [] - , chunk [ "absolute", "right-0", "top-0" ] [] - ] - - - --- DECAGON - - -decagonSvg : Svg.Svg msg -decagonSvg = - Svg.svg - [ Svg.Attributes.class "mx-auto" - , Svg.Attributes.fill "transparent" - , Svg.Attributes.height "200" - , Svg.Attributes.strokeLinejoin "miter" - , Svg.Attributes.strokeWidth "7px" - , Svg.Attributes.viewBox "0 0 200 200" - , Svg.Attributes.width "200" - ] - [ Svg.polygon - [ Svg.Attributes.points "129.665631459995,191.301425564335 70.3343685400051,191.301425564335 22.3343685400051,156.427384220077 4,100 22.334368540005,43.5726157799226 70.334368540005,8.69857443566526 129.665631459995,8.69857443566525 177.665631459995,43.5726157799226 196,100 177.665631459995,156.427384220077" ] - [] - ] diff --git a/src/Applications/UI/Page.elm b/src/Applications/UI/Page.elm index b134beb0..1d502ad1 100644 --- a/src/Applications/UI/Page.elm +++ b/src/Applications/UI/Page.elm @@ -17,8 +17,7 @@ import Url.Parser.Query as Query type Page - = Equalizer - | Index + = Index | Queue Queue.Page | Playlists Playlists.Page | Settings Settings.Page @@ -70,9 +69,6 @@ toString = toString_ : Page -> String toString_ page = case page of - Equalizer -> - "equalizer" - Index -> "" @@ -151,9 +147,6 @@ sameBase a b = ( Sources _, Sources _ ) -> True - ( Index, Equalizer ) -> - True - ( Index, Playlists _ ) -> True @@ -187,9 +180,6 @@ route = oneOf [ map Index top - -- - , map Equalizer (s "equalizer") - ----------------------------------------- -- Playlists ----------------------------------------- diff --git a/src/Applications/UI/Tracks/View.elm b/src/Applications/UI/Tracks/View.elm index 22ea24a0..e1823540 100644 --- a/src/Applications/UI/Tracks/View.elm +++ b/src/Applications/UI/Tracks/View.elm @@ -2,15 +2,17 @@ module UI.Tracks.View exposing (view) import Chunky exposing (..) import Color exposing (Color) +import Common exposing (Switch(..)) import Conditional exposing (ifThenElse) import Html exposing (Html, text) -import Html.Attributes exposing (attribute, href, placeholder, style, tabindex, target, title, value) -import Html.Events exposing (onBlur, onClick, onInput) +import Html.Attributes as A exposing (attribute, href, placeholder, style, tabindex, target, title, value) +import Html.Events as E exposing (onBlur, onClick, onInput) import Html.Events.Extra.Mouse as Mouse import Html.Ext exposing (onEnterKey) import Html.Lazy exposing (..) import Material.Icons.Round as Icons import Material.Icons.Types exposing (Coloring(..)) +import Maybe.Extra as Maybe import Playlists exposing (Playlist) import Tracks exposing (..) import UI.Kit @@ -37,7 +39,7 @@ view model = in chunk viewClasses - [ lazy7 + [ lazy8 navigation model.grouping model.favouritesOnly @@ -46,6 +48,9 @@ view model = isOnIndexPage model.extractedBackdropColor model.scene + ( model.showVolumeSlider + , model.eqSettings.volume + ) -- , if List.isEmpty model.tracks.harvested then @@ -111,17 +116,18 @@ viewClasses = [ "flex" , "flex-col" , "flex-grow" + , "relative" ] -navigation : Maybe Grouping -> Bool -> Maybe String -> Maybe Playlist -> Bool -> Maybe Color -> Scene -> Html UI.Msg -navigation maybeGrouping favouritesOnly searchTerm selectedPlaylist isOnIndexPage bgColor scene = +navigation : Maybe Grouping -> Bool -> Maybe String -> Maybe Playlist -> Bool -> Maybe Color -> Scene -> ( Bool, Float ) -> Html UI.Msg +navigation maybeGrouping favouritesOnly searchTerm selectedPlaylist isOnIndexPage bgColor scene ( showVolumeSlider, volume ) = let tabindex_ = ifThenElse isOnIndexPage 0 -1 in chunk - [ "sm:flex" ] + [ "relative", "sm:flex" ] [ ----------------------------------------- -- Part 1 ----------------------------------------- @@ -339,11 +345,62 @@ navigation maybeGrouping favouritesOnly searchTerm selectedPlaylist isOnIndexPag , Label "Queue" Hidden , NavigateToPage (Page.Queue UI.Queue.Page.Index) ) - , ( Icon Icons.volume_up + , ( if volume == 0 then + Icon Icons.volume_off + + else if volume < 0.5 then + Icon Icons.volume_down + + else + Icon Icons.volume_up , Label "Volume" Hidden - , NavigateToPage Page.Equalizer + , PerformMsg (ToggleVolumeSlider <| ifThenElse showVolumeSlider Off On) ) ] + , ----------------------------------------- + -- Part 3 + ----------------------------------------- + if showVolumeSlider then + chunk + [ "absolute" + , "bg-white" + , "px-4" + , "py-3" + , "right-0" + , "rounded-bl" + , "shadow-lg" + , "top-full" + , "z-30" + + -- Dark mode + ------------ + , "dark:bg-darkest-hour" + ] + [ chunk + [ "leading-[0px]" + , "my-1" + , "pt-px" + , "text-[0px]" + ] + [ slab + Html.input + [ A.type_ "range" + , A.min "0" + , A.max "1" + , A.step "0.0125" + , A.value (String.fromFloat volume) + + -- + , E.onBlur SaveEnclosedUserData + , E.onInput (String.toFloat >> Maybe.unwrap Bypass AdjustVolume) + ] + [ "range-slider" ] + [] + ] + ] + + else + nothing ] diff --git a/src/Applications/UI/Types.elm b/src/Applications/UI/Types.elm index 1df3aac6..042973c4 100644 --- a/src/Applications/UI/Types.elm +++ b/src/Applications/UI/Types.elm @@ -102,8 +102,8 @@ type alias Model = ----------------------------------------- -- Equalizer ----------------------------------------- - , eqKnobOperation : Maybe Equalizer.KnobOperation , eqSettings : Equalizer.Settings + , showVolumeSlider : Bool ----------------------------------------- -- Instances @@ -213,10 +213,8 @@ type Msg ----------------------------------------- -- Equalizer ----------------------------------------- - | ActivateKnob Knob Pointer.Event - | AdjustKnob Pointer.Event - | DeactivateKnob Pointer.Event - | ResetKnob Knob + | AdjustVolume Float + | ToggleVolumeSlider Switch ----------------------------------------- -- Interface ----------------------------------------- diff --git a/src/Applications/UI/View.elm b/src/Applications/UI/View.elm index b9c61fd9..18f60950 100644 --- a/src/Applications/UI/View.elm +++ b/src/Applications/UI/View.elm @@ -3,12 +3,12 @@ module UI.View exposing (view) import Alfred exposing (Alfred) import Browser import Chunky exposing (..) +import Common exposing (Switch(..)) import Conditional exposing (..) import ContextMenu exposing (ContextMenu) import Html exposing (Html, section) import Html.Attributes exposing (class, style) import Html.Events exposing (on) -import Html.Events.Extra.Pointer as Pointer import Html.Lazy as Lazy import Json.Decode import Maybe.Extra as Maybe @@ -19,7 +19,6 @@ import UI.Authentication.View as Authentication import UI.Backdrop as Backdrop import UI.Console import UI.ContextMenu -import UI.Equalizer.View as Equalizer import UI.Navigation as Navigation import UI.Notifications import UI.Page as Page @@ -52,11 +51,8 @@ body model = (if Maybe.isJust model.contextMenu || Maybe.isJust model.alfred then [ on "tap" (Json.Decode.succeed HideOverlay) ] - else if Maybe.isJust model.eqKnobOperation then - [ Pointer.onMove AdjustKnob - , Pointer.onUp DeactivateKnob - , Pointer.onCancel DeactivateKnob - ] + else if model.showVolumeSlider then + [ on "tap" (Json.Decode.succeed <| ToggleVolumeSlider Off) ] else if model.isDragging then [ class "dragging-something" @@ -157,9 +153,6 @@ defaultScreen model = -- Pages -------- , case model.page of - Page.Equalizer -> - Lazy.lazy Equalizer.view model.eqSettings - Page.Index -> nothing diff --git a/src/Css/Application.css b/src/Css/Application.css index ee845435..672c2e9c 100644 --- a/src/Css/Application.css +++ b/src/Css/Application.css @@ -8,7 +8,6 @@ @import "tailwindcss/components"; @import "tailwindcss/utilities"; -@import "Equalizer.css"; @import "Fonts.css"; @import "Logo.css"; @import "Notifications.css"; @@ -22,6 +21,7 @@ .scrolling-touch { -webkit-overflow-scrolling: touch; } + .scrolling-auto { -webkit-overflow-scrolling: auto; } @@ -32,7 +32,8 @@ /* Base ---- */ -html, body { +html, +body { height: calc(var(--vh, 1vh) * 100); overflow: hidden; overscroll-behavior: none; @@ -53,13 +54,26 @@ body { outline: none; } -.align-sub { vertical-align: sub } -.all-small-caps { font-variant-caps: all-small-caps } -.flex-basis-0 { flex-basis: 0 } -.smooth-scrolling { scroll-behavior: smooth } +.align-sub { + vertical-align: sub +} + +.all-small-caps { + font-variant-caps: all-small-caps +} + +.flex-basis-0 { + flex-basis: 0 +} + +.smooth-scrolling { + scroll-behavior: smooth +} @screen dark { - body { @apply text-gray-600; } + body { + @apply text-gray-600; + } } @@ -114,6 +128,7 @@ select:-moz-focusring { } @screen dark { + input::placeholder, textarea::placeholder { @apply text-base03; @@ -137,23 +152,42 @@ select:-moz-focusring { .loading-animation__circle { animation: loading-dash 1.5s ease-in-out infinite, - loading-colors 6s ease-in-out infinite; + loading-colors 6s ease-in-out infinite; stroke-dasharray: 1, 86.25; stroke-dashoffset: 0; transform-origin: center; } @keyframes loading-rotator { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } } @keyframes loading-colors { - 0% { stroke: rgb(248, 164, 167); } - 25% { stroke: rgb(254, 196, 24); } - 50% { stroke: rgb(72, 182, 133); } - 75% { stroke: rgb(6, 182, 239); } - 100% { stroke: rgb(248, 164, 167); } + 0% { + stroke: rgb(248, 164, 167); + } + + 25% { + stroke: rgb(254, 196, 24); + } + + 50% { + stroke: rgb(72, 182, 133); + } + + 75% { + stroke: rgb(6, 182, 239); + } + + 100% { + stroke: rgb(248, 164, 167); + } } @keyframes loading-dash { @@ -161,10 +195,12 @@ select:-moz-focusring { stroke-dasharray: 1, 86.25; stroke-dashoffset: 0; } + 50% { stroke-dasharray: 64.6875, 86.25; stroke-dashoffset: -21.5625; } + 100% { stroke-dasharray: 64.6875, 86.25; stroke-dashoffset: -86.25; @@ -176,9 +212,58 @@ select:-moz-focusring { /* Overrides --------- */ -.no-tracks-view > div { +.no-tracks-view>div { /* CSS fix for a scrolling issue: Element would appear scrolled even though it's not (virtual-dom issue probably) */ overflow: visible !important; } + + + +/* Range slider + ------------ */ + +.range-slider { + @apply appearance-none bg-gray-400 rounded w-24; + height: 4px; +} + +.range-slider::-webkit-slider-thumb { + @apply appearance-none bg-base02; + border-radius: 100%; + height: 10px; + width: 10px +} + +.range-slider::-moz-range-thumb { + @apply appearance-none bg-darkest-hour; + border-radius: 100%; + height: 10px; + width: 10px +} + +.range-slider::-ms-thumb { + @apply appearance-none bg-darkest-hour; + border-radius: 100%; + height: 10px; + width: 10px +} + +@screen dark { + .range-slider { + @apply bg-base01; + } + + .range-slider::-webkit-slider-thumb { + @apply bg-base07; + } + + .range-slider::-moz-range-thumb { + @apply bg-base07; + } + + .range-slider::-ms-thumb { + @apply bg-base07; + } +} \ No newline at end of file diff --git a/src/Css/Equalizer.css b/src/Css/Equalizer.css deleted file mode 100644 index 6a43abc2..00000000 --- a/src/Css/Equalizer.css +++ /dev/null @@ -1,60 +0,0 @@ -:root { - --knob-size: 36px; -} - - - -/* Knob - ---- */ - -.equalizer .knob { - box-shadow: inset 0 0 5px 1px rgba(119, 119, 119, 0.35); - height: var(--knob-size); - width: var(--knob-size); -} - -.equalizer .knob svg { - height: calc(100% - 8px); - margin-top: 4px; - stroke: rgba(119, 119, 119, 0.7); - width: calc(100% - 8px); -} - -.equalizer .knob .layer-a { - box-shadow: 0 0 6px 1px rgba(119, 119, 119, 1); - margin: 8px; -} - -.equalizer .knob .layer-b { - background-color: rgba(119, 119, 119, 0.8); - height: 9px; - width: 2px; -} - - - -/* Knob Label - ---------- */ - -.equalizer .knob-label { - font-size: 9.5px; -} - - - -/* Knob Lines - ---------- */ - -.equalizer .knob-lines { - top: calc(var(--knob-size) / -4.5); - width: var(--knob-size); -} - -.equalizer .knob-lines > div { - background-color: rgba(119, 119, 119, 0.8); - height: 9px; - width: 1px; -} - -.equalizer .knob-lines > div:first-child { transform: rotate(45deg) } -.equalizer .knob-lines > div:last-child { transform: rotate(-45deg) }