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
$ 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; [
elmPackages.elm
elmPackages.elm-format
elmPackages.elm-live
];
}

View file

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

View file

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

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