Begin styling efforts

Start styling the Chord Drill Sergeant for mobile devices because that is that
device on which I will primarily use CDS.

I'm also deleting the debugger related code. I would like to support a debugger,
but I'm not currently using this one, so I am going to remove it to keep things
slender.

- Introduce TailwindCSS, which also introduced elm-live, index.html, index.css
- Add mobile-first styling for the preferences modal
- Remove unused code
This commit is contained in:
William Carroll 2020-04-13 22:39:15 +01:00
parent a64601cc05
commit 1d427c4921
8 changed files with 264 additions and 126 deletions

View file

@ -1 +1,3 @@
/elm-stuff /elm-stuff
/elm.js
/output.css

View file

@ -53,5 +53,5 @@ in which you can develop:
```shell ```shell
$ nix-shell $ nix-shell
$ elm reactor $ elm-live -- src/Main.elm --output=elm.js
``` ```

View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -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>

View file

@ -4,5 +4,6 @@ in pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
elmPackages.elm elmPackages.elm
elmPackages.elm-format elmPackages.elm-format
elmPackages.elm-live
]; ];
} }

View file

@ -1,17 +1,16 @@
module Main exposing (main) module Main exposing (main)
import Browser import Browser
import ChordInspector
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (..) import Html.Events exposing (..)
import NoteInspector
import Piano import Piano
import Random import Random
import Random.List import Random.List
import Tempo import Tempo
import Theory import Theory
import Time exposing (..) import Time exposing (..)
import UI
type alias Model = type alias Model =
@ -26,13 +25,15 @@ type alias Model =
, firstNote : Theory.Note , firstNote : Theory.Note
, lastNote : Theory.Note , lastNote : Theory.Note
, practiceMode : PracticeMode , practiceMode : PracticeMode
, debug : , view : View
{ enable : Bool
, inspectChord : Bool
}
} }
type View
= Preferences
| Practice
{-| Control the type of practice you'd like. {-| Control the type of practice you'd like.
-} -}
type PracticeMode type PracticeMode
@ -48,7 +49,6 @@ type Msg
| IncreaseTempo | IncreaseTempo
| DecreaseTempo | DecreaseTempo
| SetTempo String | SetTempo String
| ToggleInspectChord
| ToggleInversion Theory.ChordInversion | ToggleInversion Theory.ChordInversion
| ToggleChordType Theory.ChordType | ToggleChordType Theory.ChordType
| TogglePitchClass Theory.PitchClass | TogglePitchClass Theory.PitchClass
@ -84,7 +84,7 @@ init : Model
init = init =
let let
( firstNote, lastNote ) = ( firstNote, lastNote ) =
( Theory.C3, Theory.C5 ) ( Theory.A1, Theory.C8 )
inversions = inversions =
Theory.allInversions Theory.allInversions
@ -96,7 +96,7 @@ init =
Theory.allPitchClasses Theory.allPitchClasses
keys = keys =
Theory.allKeys []
practiceMode = practiceMode =
KeyMode KeyMode
@ -121,13 +121,10 @@ init =
, whitelistedKeys = keys , whitelistedKeys = keys
, selectedChord = Nothing , selectedChord = Nothing
, isPaused = True , isPaused = True
, tempo = 60 , tempo = 30
, firstNote = firstNote , firstNote = firstNote
, lastNote = lastNote , lastNote = lastNote
, debug = , view = Preferences
{ enable = False
, inspectChord = True
}
} }
@ -212,16 +209,6 @@ update msg model =
, Cmd.none , Cmd.none
) )
ToggleInspectChord ->
( { model
| debug =
{ inspectChord = not model.debug.inspectChord
, enable = model.debug.enable
}
}
, Cmd.none
)
ToggleChordType chordType -> ToggleChordType chordType ->
let let
chordTypes = chordTypes =
@ -331,33 +318,6 @@ playPause { isPaused } =
button [ onClick Pause ] [ text "Pause" ] 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 : List Theory.ChordType -> Html Msg
chordTypeCheckboxes chordTypes = chordTypeCheckboxes chordTypes =
ul [] ul []
@ -396,45 +356,71 @@ inversionCheckboxes inversions =
) )
keyCheckboxes : List Theory.Key -> Html Msg selectKey :
keyCheckboxes keys = 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 [] div []
[ h2 [] [ text "Choose Key" ] [ h2 [ class "text-center py-10 text-5xl" ] [ text "Select Keys" ]
, button [ onClick SelectAllKeys ] [ text "Select all" ]
, button [ onClick DeselectAllKeys ] [ text "Deselect all" ]
, ul [] , ul []
(Theory.allKeys (Theory.allPitchClasses
|> List.map |> List.map
(\key -> (\pitchClass ->
li [] selectKey model
[ label [] [ text (Theory.viewKey key) ] { pitchClass = pitchClass
, input , majorKey = { pitchClass = pitchClass, mode = Theory.MajorMode }
[ type_ "checkbox" , minorKey = { pitchClass = pitchClass, mode = Theory.MinorMode }
, onClick (ToggleKey key) , bluesKey = { pitchClass = pitchClass, mode = Theory.BluesMode }
, checked (List.member key keys) }
]
[]
]
) )
) )
] ]
displayChord : displayChord :
{ debug : Bool { chord : Theory.Chord
, chord : Theory.Chord
, firstNote : Theory.Note , firstNote : Theory.Note
, lastNote : Theory.Note , lastNote : Theory.Note
} }
-> Html Msg -> Html Msg
displayChord { debug, chord, firstNote, lastNote } = displayChord { chord, firstNote, lastNote } =
div [] div []
[ if debug then [ p [] [ text (Theory.viewChord chord) ]
ChordInspector.render chord
else
span [] []
, p [] [ text (Theory.viewChord chord) ]
, case Theory.notesForChord chord of , case Theory.notesForChord chord of
Just x -> Just x ->
Piano.render Piano.render
@ -448,57 +434,65 @@ displayChord { debug, chord, firstNote, lastNote } =
] ]
view : Model -> Html Msg practiceModeButtons : Model -> Html Msg
view model = practiceModeButtons model =
div [] 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.render
{ tempo = model.tempo { tempo = model.tempo
, handleIncrease = IncreaseTempo
, handleDecrease = DecreaseTempo
, handleInput = SetTempo , handleInput = SetTempo
} }
, div [] , practiceModeButtons model
[ 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" ]
]
, case model.practiceMode of , case model.practiceMode of
KeyMode -> KeyMode ->
keyCheckboxes model.whitelistedKeys keyCheckboxes model
FineTuneMode -> FineTuneMode ->
div [] div []
[ pitchClassCheckboxes model.whitelistedPitchClasses [ inversionCheckboxes model.whitelistedInversions
, inversionCheckboxes model.whitelistedInversions
, chordTypeCheckboxes model.whitelistedChordTypes , 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 , case model.selectedChord of
Just chord -> Just chord ->
displayChord displayChord
{ debug = model.debug.inspectChord { chord = chord
, chord = chord
, firstNote = model.firstNote , firstNote = model.firstNote
, lastNote = model.lastNote , 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. {-| For now, I'm just dumping things onto the page to sketch ideas.
-} -}
main = main =

View file

@ -3,25 +3,22 @@ module Tempo exposing (render)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (..) import Html.Events exposing (..)
import UI
type alias Props msg = type alias Props msg =
{ tempo : Int { tempo : Int
, handleIncrease : msg
, handleDecrease : msg
, handleInput : String -> msg , handleInput : String -> msg
} }
render : Props msg -> Html msg render : Props msg -> Html msg
render { tempo, handleIncrease, handleDecrease, handleInput } = render { tempo, handleInput } =
div [] div [ class "text-center" ]
[ p [] [ text (String.fromInt tempo ++ " BPM") ] [ p [ class "text-5xl py-10" ] [ text (String.fromInt tempo ++ " BPM") ]
, button [ onClick handleDecrease ] [ text "Slower" ] , UI.textField
, input { placeholderText = "Set tempo..."
[ onInput handleInput , handleInput = handleInput
, placeholder "Set tempo..." , classes = []
] }
[]
, button [ onClick handleIncrease ] [ text "Faster" ]
] ]

View file

@ -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
]
[]