diff options
-rw-r--r-- | website/sandbox/chord-drill-sergeant/.gitignore | 4 | ||||
-rw-r--r-- | website/sandbox/chord-drill-sergeant/README.md | 2 | ||||
-rw-r--r-- | website/sandbox/chord-drill-sergeant/index.css | 3 | ||||
-rw-r--r-- | website/sandbox/chord-drill-sergeant/index.html | 15 | ||||
-rw-r--r-- | website/sandbox/chord-drill-sergeant/shell.nix | 1 | ||||
-rw-r--r-- | website/sandbox/chord-drill-sergeant/src/Main.elm | 228 | ||||
-rw-r--r-- | website/sandbox/chord-drill-sergeant/src/Tempo.elm | 21 | ||||
-rw-r--r-- | website/sandbox/chord-drill-sergeant/src/UI.elm | 116 |
8 files changed, 264 insertions, 126 deletions
diff --git a/website/sandbox/chord-drill-sergeant/.gitignore b/website/sandbox/chord-drill-sergeant/.gitignore index 8b0d053e4e35..aa247dd183c9 100644 --- a/website/sandbox/chord-drill-sergeant/.gitignore +++ b/website/sandbox/chord-drill-sergeant/.gitignore @@ -1 +1,3 @@ -/elm-stuff \ No newline at end of file +/elm-stuff +/elm.js +/output.css \ No newline at end of file diff --git a/website/sandbox/chord-drill-sergeant/README.md b/website/sandbox/chord-drill-sergeant/README.md index 0f9dd4d6bc6e..9ed3dae91a68 100644 --- a/website/sandbox/chord-drill-sergeant/README.md +++ b/website/sandbox/chord-drill-sergeant/README.md @@ -53,5 +53,5 @@ in which you can develop: ```shell $ nix-shell -$ elm reactor +$ elm-live -- src/Main.elm --output=elm.js ``` diff --git a/website/sandbox/chord-drill-sergeant/index.css b/website/sandbox/chord-drill-sergeant/index.css new file mode 100644 index 000000000000..b5c61c956711 --- /dev/null +++ b/website/sandbox/chord-drill-sergeant/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/website/sandbox/chord-drill-sergeant/index.html b/website/sandbox/chord-drill-sergeant/index.html new file mode 100644 index 000000000000..d2218ea1905a --- /dev/null +++ b/website/sandbox/chord-drill-sergeant/index.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <title>Chord Drill Sergeant</title> + <link rel="stylesheet" href="./output.css" /> + <script src="./elm.js"></script> + </head> + <body class="font-serif"> + <div id="mount"></div> + <script> + Elm.Main.init({node: document.getElementById("mount")}); + </script> + </body> +</html> diff --git a/website/sandbox/chord-drill-sergeant/shell.nix b/website/sandbox/chord-drill-sergeant/shell.nix index 584b6c4a9e84..6f1c8ee23b30 100644 --- a/website/sandbox/chord-drill-sergeant/shell.nix +++ b/website/sandbox/chord-drill-sergeant/shell.nix @@ -4,5 +4,6 @@ in pkgs.mkShell { buildInputs = with pkgs; [ elmPackages.elm elmPackages.elm-format + elmPackages.elm-live ]; } diff --git a/website/sandbox/chord-drill-sergeant/src/Main.elm b/website/sandbox/chord-drill-sergeant/src/Main.elm index ebbc523333af..f13412a0a9f4 100644 --- a/website/sandbox/chord-drill-sergeant/src/Main.elm +++ b/website/sandbox/chord-drill-sergeant/src/Main.elm @@ -1,17 +1,16 @@ module Main exposing (main) import Browser -import ChordInspector import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) -import NoteInspector import Piano import Random import Random.List import Tempo import Theory import Time exposing (..) +import UI type alias Model = @@ -26,13 +25,15 @@ type alias Model = , firstNote : Theory.Note , lastNote : Theory.Note , practiceMode : PracticeMode - , debug : - { enable : Bool - , inspectChord : Bool - } + , view : View } +type View + = Preferences + | Practice + + {-| Control the type of practice you'd like. -} type PracticeMode @@ -48,7 +49,6 @@ type Msg | IncreaseTempo | DecreaseTempo | SetTempo String - | ToggleInspectChord | ToggleInversion Theory.ChordInversion | ToggleChordType Theory.ChordType | TogglePitchClass Theory.PitchClass @@ -84,7 +84,7 @@ init : Model init = let ( firstNote, lastNote ) = - ( Theory.C3, Theory.C5 ) + ( Theory.A1, Theory.C8 ) inversions = Theory.allInversions @@ -96,7 +96,7 @@ init = Theory.allPitchClasses keys = - Theory.allKeys + [] practiceMode = KeyMode @@ -121,13 +121,10 @@ init = , whitelistedKeys = keys , selectedChord = Nothing , isPaused = True - , tempo = 60 + , tempo = 30 , firstNote = firstNote , lastNote = lastNote - , debug = - { enable = False - , inspectChord = True - } + , view = Preferences } @@ -212,16 +209,6 @@ update msg model = , Cmd.none ) - ToggleInspectChord -> - ( { model - | debug = - { inspectChord = not model.debug.inspectChord - , enable = model.debug.enable - } - } - , Cmd.none - ) - ToggleChordType chordType -> let chordTypes = @@ -331,33 +318,6 @@ playPause { isPaused } = button [ onClick Pause ] [ text "Pause" ] -debugger : Html Msg -debugger = - fieldset [] - [ label [] [ text "Inspect Chord" ] - , input [ type_ "checkbox", onClick ToggleInspectChord, checked init.debug.inspectChord ] [] - ] - - -pitchClassCheckboxes : List Theory.PitchClass -> Html Msg -pitchClassCheckboxes pitchClasses = - ul [] - (Theory.allPitchClasses - |> List.map - (\pitchClass -> - li [] - [ label [] [ text (Theory.viewPitchClass pitchClass) ] - , input - [ type_ "checkbox" - , onClick (TogglePitchClass pitchClass) - , checked (List.member pitchClass pitchClasses) - ] - [] - ] - ) - ) - - chordTypeCheckboxes : List Theory.ChordType -> Html Msg chordTypeCheckboxes chordTypes = ul [] @@ -396,45 +356,71 @@ inversionCheckboxes inversions = ) -keyCheckboxes : List Theory.Key -> Html Msg -keyCheckboxes keys = +selectKey : + Model + -> + { pitchClass : Theory.PitchClass + , majorKey : Theory.Key + , minorKey : Theory.Key + , bluesKey : Theory.Key + } + -> Html Msg +selectKey model { pitchClass, majorKey, minorKey, bluesKey } = + let + active key = + List.member key model.whitelistedKeys + in + div [ class "flex pt-0" ] + [ p [ class "text-gray-500 text-center text-5xl flex-1 py-10" ] [ text (Theory.viewPitchClass pitchClass) ] + , UI.textToggleButton + { label = "major" + , handleClick = ToggleKey majorKey + , classes = [ "flex-1" ] + , toggled = active majorKey + } + , UI.textToggleButton + { label = "minor" + , handleClick = ToggleKey minorKey + , classes = [ "flex-1" ] + , toggled = active minorKey + } + , UI.textToggleButton + { label = "blues" + , handleClick = ToggleKey bluesKey + , classes = [ "flex-1" ] + , toggled = active bluesKey + } + ] + + +keyCheckboxes : Model -> Html Msg +keyCheckboxes model = div [] - [ h2 [] [ text "Choose Key" ] - , button [ onClick SelectAllKeys ] [ text "Select all" ] - , button [ onClick DeselectAllKeys ] [ text "Deselect all" ] + [ h2 [ class "text-center py-10 text-5xl" ] [ text "Select Keys" ] , ul [] - (Theory.allKeys + (Theory.allPitchClasses |> List.map - (\key -> - li [] - [ label [] [ text (Theory.viewKey key) ] - , input - [ type_ "checkbox" - , onClick (ToggleKey key) - , checked (List.member key keys) - ] - [] - ] + (\pitchClass -> + selectKey model + { pitchClass = pitchClass + , majorKey = { pitchClass = pitchClass, mode = Theory.MajorMode } + , minorKey = { pitchClass = pitchClass, mode = Theory.MinorMode } + , bluesKey = { pitchClass = pitchClass, mode = Theory.BluesMode } + } ) ) ] displayChord : - { debug : Bool - , chord : Theory.Chord + { chord : Theory.Chord , firstNote : Theory.Note , lastNote : Theory.Note } -> Html Msg -displayChord { debug, chord, firstNote, lastNote } = +displayChord { chord, firstNote, lastNote } = div [] - [ if debug then - ChordInspector.render chord - - else - span [] [] - , p [] [ text (Theory.viewChord chord) ] + [ p [] [ text (Theory.viewChord chord) ] , case Theory.notesForChord chord of Just x -> Piano.render @@ -448,57 +434,65 @@ displayChord { debug, chord, firstNote, lastNote } = ] -view : Model -> Html Msg -view model = - div [] +practiceModeButtons : Model -> Html Msg +practiceModeButtons model = + div [ class "text-center" ] + [ h2 [ class "py-10 text-5xl" ] [ text "Practice Mode" ] + , div [ class "flex pb-6" ] + [ UI.simpleButton + { label = "Key" + , classes = [ "flex-1", "rounded-r-none" ] + , handleClick = SetPracticeMode KeyMode + , color = + if model.practiceMode == KeyMode then + UI.Primary + + else + UI.Secondary + } + , UI.simpleButton + { label = "Fine Tune" + , handleClick = SetPracticeMode FineTuneMode + , classes = [ "flex-1", "rounded-l-none" ] + , color = + if model.practiceMode == FineTuneMode then + UI.Primary + + else + UI.Secondary + } + ] + ] + + +preferences : Model -> Html Msg +preferences model = + div [ class "pt-10 pb-20 px-10" ] [ Tempo.render { tempo = model.tempo - , handleIncrease = IncreaseTempo - , handleDecrease = DecreaseTempo , handleInput = SetTempo } - , div [] - [ h2 [] [ text "Practice Mode" ] - , input - [ type_ "radio" - , id "key-mode" - , name "key-mode" - , checked (model.practiceMode == KeyMode) - , onClick (SetPracticeMode KeyMode) - ] - [] - , label [ for "key-mode" ] [ text "Key Mode" ] - , input - [ type_ "radio" - , id "fine-tune-mode" - , name "fine-tune-mode" - , checked (model.practiceMode == FineTuneMode) - , onClick (SetPracticeMode FineTuneMode) - ] - [] - , label [ for "fine-tune-mode" ] [ text "Fine-tuning Mode" ] - ] + , practiceModeButtons model , case model.practiceMode of KeyMode -> - keyCheckboxes model.whitelistedKeys + keyCheckboxes model FineTuneMode -> div [] - [ pitchClassCheckboxes model.whitelistedPitchClasses - , inversionCheckboxes model.whitelistedInversions + [ inversionCheckboxes model.whitelistedInversions , chordTypeCheckboxes model.whitelistedChordTypes ] - , playPause model - , if model.debug.enable then - debugger + ] - else - span [] [] + +practice : Model -> Html Msg +practice model = + div [] + [ playPause model , case model.selectedChord of Just chord -> displayChord - { debug = model.debug.inspectChord - , chord = chord + { chord = chord , firstNote = model.firstNote , lastNote = model.lastNote } @@ -508,6 +502,16 @@ view model = ] +view : Model -> Html Msg +view model = + case model.view of + Preferences -> + preferences model + + Practice -> + practice model + + {-| For now, I'm just dumping things onto the page to sketch ideas. -} main = diff --git a/website/sandbox/chord-drill-sergeant/src/Tempo.elm b/website/sandbox/chord-drill-sergeant/src/Tempo.elm index 270cc5bd6dc6..50485c4c0aba 100644 --- a/website/sandbox/chord-drill-sergeant/src/Tempo.elm +++ b/website/sandbox/chord-drill-sergeant/src/Tempo.elm @@ -3,25 +3,22 @@ module Tempo exposing (render) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) +import UI type alias Props msg = { tempo : Int - , handleIncrease : msg - , handleDecrease : msg , handleInput : String -> msg } render : Props msg -> Html msg -render { tempo, handleIncrease, handleDecrease, handleInput } = - div [] - [ p [] [ text (String.fromInt tempo ++ " BPM") ] - , button [ onClick handleDecrease ] [ text "Slower" ] - , input - [ onInput handleInput - , placeholder "Set tempo..." - ] - [] - , button [ onClick handleIncrease ] [ text "Faster" ] +render { tempo, handleInput } = + div [ class "text-center" ] + [ p [ class "text-5xl py-10" ] [ text (String.fromInt tempo ++ " BPM") ] + , UI.textField + { placeholderText = "Set tempo..." + , handleInput = handleInput + , classes = [] + } ] diff --git a/website/sandbox/chord-drill-sergeant/src/UI.elm b/website/sandbox/chord-drill-sergeant/src/UI.elm new file mode 100644 index 000000000000..dff645f4796a --- /dev/null +++ b/website/sandbox/chord-drill-sergeant/src/UI.elm @@ -0,0 +1,116 @@ +module UI exposing (..) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) + + +type Color + = Primary + | Secondary + + +bgForColor : Color -> String +bgForColor color = + case color of + Primary -> + "bg-gray-600" + + Secondary -> + "bg-gray-300" + + +textForColor : Color -> String +textForColor color = + case color of + Primary -> + "text-white" + + Secondary -> + "text-black" + + +tw : List String -> String +tw styles = + String.join " " styles + + +simpleButton : + { label : String + , handleClick : msg + , color : Color + , classes : List String + } + -> Html msg +simpleButton { label, handleClick, color, classes } = + let + buttonClasses = + [ bgForColor color + , textForColor color + , "py-10" + , "px-20" + , "text-5xl" + , "rounded-lg" + ] + in + button + [ class (tw <| List.concat [ buttonClasses, classes ]) + , onClick handleClick + ] + [ text label ] + + +textToggleButton : + { label : String + , handleClick : msg + , classes : List String + , toggled : Bool + } + -> Html msg +textToggleButton { label, toggled, handleClick, classes } = + let + ( textColor, textTreatment ) = + if toggled then + ( "text-red-600", "underline" ) + + else + ( "text-black", "no-underline" ) + + buttonClasses = + [ textColor + , textTreatment + , "py-10" + , "px-10" + , "text-5xl" + ] + in + button + [ class (tw <| List.concat [ buttonClasses, classes ]) + , onClick handleClick + ] + [ text label ] + + +textField : + { placeholderText : String + , handleInput : String -> msg + , classes : List String + } + -> Html msg +textField { placeholderText, handleInput, classes } = + let + inputClasses = + [ "text-5xl" + , "w-full" + , "py-10" + , "px-16" + , "border" + , "rounded-lg" + ] + in + input + [ class (tw <| List.concat [ inputClasses, classes ]) + , onInput handleInput + , placeholder placeholderText + ] + [] |