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:
parent
a64601cc05
commit
1d427c4921
8 changed files with 264 additions and 126 deletions
|
@ -1 +1,3 @@
|
|||
/elm-stuff
|
||||
/elm-stuff
|
||||
/elm.js
|
||||
/output.css
|
|
@ -53,5 +53,5 @@ in which you can develop:
|
|||
|
||||
```shell
|
||||
$ nix-shell
|
||||
$ elm reactor
|
||||
$ elm-live -- src/Main.elm --output=elm.js
|
||||
```
|
||||
|
|
3
website/sandbox/chord-drill-sergeant/index.css
Normal file
3
website/sandbox/chord-drill-sergeant/index.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
15
website/sandbox/chord-drill-sergeant/index.html
Normal file
15
website/sandbox/chord-drill-sergeant/index.html
Normal 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>
|
|
@ -4,5 +4,6 @@ in pkgs.mkShell {
|
|||
buildInputs = with pkgs; [
|
||||
elmPackages.elm
|
||||
elmPackages.elm-format
|
||||
elmPackages.elm-live
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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 = []
|
||||
}
|
||||
]
|
||||
|
|
116
website/sandbox/chord-drill-sergeant/src/UI.elm
Normal file
116
website/sandbox/chord-drill-sergeant/src/UI.elm
Normal 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
|
||||
]
|
||||
[]
|
Loading…
Reference in a new issue