From 5485d2501f1ddb4c052a4649aab75cf81e0238a2 Mon Sep 17 00:00:00 2001 From: Cyril Gourgouillon Date: Fri, 24 Nov 2023 00:18:11 -0500 Subject: [PATCH] Add Chords feature (#10) --- src/App.tsx | 21 ++++++-- src/components/ChordsList/ChordsList.tsx | 12 +++++ src/components/ChordsList/index.ts | 1 + src/components/NotesList/NotesList.tsx | 4 +- src/components/index.ts | 1 + src/config/AppPages.ts | 4 ++ src/config/Chords.ts | 17 ++++++ src/config/GuitarStrings.ts | 16 +++--- src/config/Notes.ts | 34 ++++++------ src/config/constants/chords.ts | 3 ++ src/config/constants/index.ts | 1 + src/config/index.ts | 6 ++- src/pages/ChordsPage.tsx | 69 ++++++++++++++++++++++++ src/pages/NotesPage.tsx | 4 +- src/pages/index.ts | 3 +- src/services/chordService.ts | 28 ++++++++++ src/services/index.ts | 5 +- src/services/noteService.ts | 7 ++- src/services/stringService.ts | 3 +- src/utils/random.ts | 3 ++ src/utils/shuffle.ts | 2 +- 21 files changed, 206 insertions(+), 38 deletions(-) create mode 100644 src/components/ChordsList/ChordsList.tsx create mode 100644 src/components/ChordsList/index.ts create mode 100644 src/config/AppPages.ts create mode 100644 src/config/Chords.ts create mode 100644 src/config/constants/chords.ts create mode 100644 src/pages/ChordsPage.tsx create mode 100644 src/services/chordService.ts create mode 100644 src/utils/random.ts diff --git a/src/App.tsx b/src/App.tsx index ac5d31d..c6438cb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,26 @@ -import { ChakraProvider } from "@chakra-ui/react"; -import { NotesPage } from "./pages"; +import { Button, ChakraProvider } from "@chakra-ui/react"; +import { ChordsPage, NotesPage } from "./pages"; +import { AppPages } from "./config"; +import { useState } from "react"; const App = () => { + const [selectedPage, setSelectedPage] = useState(AppPages.notes); + return (
- + {selectedPage === AppPages.notes && ( + + )} + {selectedPage === AppPages.chords && ( + + )} + {selectedPage === AppPages.notes && } + {selectedPage === AppPages.chords && }
{"Made with ❤️ by "} { + return ( +
+ {chords.map((chord: Chord, index: number) => ( +
{chordToString(chord)}
+ ))} +
+ ); +}; diff --git a/src/components/ChordsList/index.ts b/src/components/ChordsList/index.ts new file mode 100644 index 0000000..56d82bc --- /dev/null +++ b/src/components/ChordsList/index.ts @@ -0,0 +1 @@ +export * from './ChordsList'; diff --git a/src/components/NotesList/NotesList.tsx b/src/components/NotesList/NotesList.tsx index a390af6..a895194 100644 --- a/src/components/NotesList/NotesList.tsx +++ b/src/components/NotesList/NotesList.tsx @@ -9,7 +9,9 @@ export const NotesList = ({ }) => { return ( <> -
{GuitarStringDecorator}
+
+ {GuitarStringDecorator} +
{notes.map((note: Note, index: number) => (
{note}
diff --git a/src/components/index.ts b/src/components/index.ts index be78984..16198b8 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1 +1,2 @@ export * from './NotesList'; +export * from './ChordsList'; diff --git a/src/config/AppPages.ts b/src/config/AppPages.ts new file mode 100644 index 0000000..eaeeb05 --- /dev/null +++ b/src/config/AppPages.ts @@ -0,0 +1,4 @@ +export enum AppPages { + notes = "Notes", + chords = "Chords", +} diff --git a/src/config/Chords.ts b/src/config/Chords.ts new file mode 100644 index 0000000..66d9578 --- /dev/null +++ b/src/config/Chords.ts @@ -0,0 +1,17 @@ +import { Note } from "."; + +export enum ChordsType { + Major = "", + Minor = "m", + MajorSeventh = "maj7", + MinorSeventh = "min7", +} + +export type ChordType = keyof typeof ChordsType; + +export const chords = Object.values(ChordsType) as unknown as ChordType[]; + +export type Chord = { + note: Note; + chordType: ChordType; +}; diff --git a/src/config/GuitarStrings.ts b/src/config/GuitarStrings.ts index b57dcf6..e03991c 100644 --- a/src/config/GuitarStrings.ts +++ b/src/config/GuitarStrings.ts @@ -1,12 +1,14 @@ export enum GuitarStrings { - String1 = 'String 1', - String2 = 'String 2', - String3 = 'String 3', - String4 = 'String 4', - String5 = 'String 5', - String6 = 'String 6', + String1 = "String 1", + String2 = "String 2", + String3 = "String 3", + String4 = "String 4", + String5 = "String 5", + String6 = "String 6", } export type GuitarString = keyof typeof GuitarStrings; -export const guitarStrings = Object.values(GuitarStrings) as unknown as GuitarString[]; +export const guitarStrings = Object.values( + GuitarStrings +) as unknown as GuitarString[]; diff --git a/src/config/Notes.ts b/src/config/Notes.ts index 88cf1a9..70ad368 100644 --- a/src/config/Notes.ts +++ b/src/config/Notes.ts @@ -1,21 +1,21 @@ export enum Notes { - C = 'C', - Csharp = 'C#', - Dbemol = 'Db', - D = 'D', - Dsharp = 'D#', - Ebemol = 'Eb', - E = 'E', - F = 'F', - Fsharp = 'F#', - Gbemol = 'Gb', - G = 'G', - Gsharp = 'G#', - Abemol = 'Ab', - A = 'A', - Asharp = 'A#', - Bbemol = 'Bb', - B = 'B', + C = "C", + Csharp = "C#", + Dbemol = "Db", + D = "D", + Dsharp = "D#", + Ebemol = "Eb", + E = "E", + F = "F", + Fsharp = "F#", + Gbemol = "Gb", + G = "G", + Gsharp = "G#", + Abemol = "Ab", + A = "A", + Asharp = "A#", + Bbemol = "Bb", + B = "B", } export type Note = keyof typeof Notes; diff --git a/src/config/constants/chords.ts b/src/config/constants/chords.ts new file mode 100644 index 0000000..683d7e0 --- /dev/null +++ b/src/config/constants/chords.ts @@ -0,0 +1,3 @@ +export const DEFAULT_NUMBER_OF_CHORD = 3; +export const CHORDS_LIST_MAX = 3; +export const CHORDS_LIST_MIN = 1; diff --git a/src/config/constants/index.ts b/src/config/constants/index.ts index 0329d15..1f3564e 100644 --- a/src/config/constants/index.ts +++ b/src/config/constants/index.ts @@ -1 +1,2 @@ export * from './notes'; +export * from './chords'; diff --git a/src/config/index.ts b/src/config/index.ts index 052b0c0..18ee97f 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,2 +1,4 @@ -export * from './Notes'; -export * from './GuitarStrings'; +export * from "./Notes"; +export * from "./GuitarStrings"; +export * from "./Chords"; +export * from "./AppPages"; diff --git a/src/pages/ChordsPage.tsx b/src/pages/ChordsPage.tsx new file mode 100644 index 0000000..802d80a --- /dev/null +++ b/src/pages/ChordsPage.tsx @@ -0,0 +1,69 @@ +import { useState } from "react"; +import { Button, IconButton } from "@chakra-ui/react"; + +import { FaPlus, FaMinus } from "react-icons/fa"; + +import { ChordsList } from "../components"; + +import { getListOfRandomChords, isValidChordCountList } from "../services"; +import { + DEFAULT_NUMBER_OF_CHORD, + CHORDS_LIST_MAX, + CHORDS_LIST_MIN, +} from "../config/constants"; + +export const ChordsPage = () => { + const [chords, setChords] = useState( + getListOfRandomChords(DEFAULT_NUMBER_OF_CHORD) + ); + + const [numberOfChordDisplayed, setNumberOfChordDisplayed] = useState( + DEFAULT_NUMBER_OF_CHORD + ); + + const handleGetRandomChordOnClick = () => { + setChords(getListOfRandomChords(numberOfChordDisplayed)); + }; + + const handleChangeNumberOfChordDisplayed = (step: number) => { + setNumberOfChordDisplayed((prevState: number) => { + const value = prevState + step; + if (isValidChordCountList(value)) { + return value; + } + return prevState; + }); + }; + + return ( +
+
RANDOM CHORDS GENERATOR
+
+
+ +
+
+ } + variant="outline" + onClick={() => handleChangeNumberOfChordDisplayed(-1)} + disabled={numberOfChordDisplayed === CHORDS_LIST_MIN} + /> + + } + variant="outline" + onClick={() => handleChangeNumberOfChordDisplayed(1)} + disabled={numberOfChordDisplayed === CHORDS_LIST_MAX} + /> +
+
+
+
+
+ ); +}; diff --git a/src/pages/NotesPage.tsx b/src/pages/NotesPage.tsx index e410f29..52d145e 100644 --- a/src/pages/NotesPage.tsx +++ b/src/pages/NotesPage.tsx @@ -7,7 +7,7 @@ import { FaPlus, FaMinus } from "react-icons/fa"; import { GuitarString, Note } from "../config"; import { NotesList } from "../components"; -import { getListOfRandomNotes, getRandomString, isValidCountList } from "../services"; +import { getListOfRandomNotes, getRandomString, isValidNoteCountList } from "../services"; import { DEFAULT_NUMBER_OF_NOTE, NOTES_LIST_MAX, NOTES_LIST_MIN } from "../config/constants"; export const NotesPage = () => { @@ -32,7 +32,7 @@ export const NotesPage = () => { const handleChangeNumberOfNoteDisplayed = (step: number) => { setNumberOfNoteDisplayed((prevState: number) => { const value = prevState + step; - if (isValidCountList(value)) { + if (isValidNoteCountList(value)) { return value; } return prevState; diff --git a/src/pages/index.ts b/src/pages/index.ts index d8e7cdb..b298471 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -1 +1,2 @@ -export * from './NotesPage'; +export * from "./NotesPage"; +export * from "./ChordsPage"; diff --git a/src/services/chordService.ts b/src/services/chordService.ts new file mode 100644 index 0000000..754c548 --- /dev/null +++ b/src/services/chordService.ts @@ -0,0 +1,28 @@ +import { getRandomNote } from "./"; +import { Chord, ChordType, chords } from "../config"; +import { getRandomItemFrom } from "../utils/random"; +import { CHORDS_LIST_MAX, CHORDS_LIST_MIN } from "../config/constants"; + +export const getRandomChordType = (): ChordType => { + return getRandomItemFrom(chords); +}; + +export const getRandomChord = (): Chord => { + return { note: getRandomNote(), chordType: getRandomChordType() } as Chord; +}; + +export const chordToString = (chord: Chord): string => { + return `${chord.note}${chord.chordType}`; +} + +export const getListOfRandomChords = (count: number): Chord[] => { + const randomChords: Chord[] = []; + for(let i = 0; i < count; i++) { + randomChords.push(getRandomChord()); + } + return randomChords; +}; + +export const isValidChordCountList = (count: number): boolean => { + return count >= CHORDS_LIST_MIN && count <= CHORDS_LIST_MAX +} diff --git a/src/services/index.ts b/src/services/index.ts index c996077..6746f5d 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,2 +1,3 @@ -export * from './noteService' -export * from './stringService' +export * from "./noteService"; +export * from "./stringService"; +export * from "./chordService"; diff --git a/src/services/noteService.ts b/src/services/noteService.ts index 612cf6b..ec4c4b6 100644 --- a/src/services/noteService.ts +++ b/src/services/noteService.ts @@ -1,5 +1,6 @@ import { Note, notes } from "../config"; import { NOTES_LIST_MAX, NOTES_LIST_MIN } from "../config/constants"; +import { getRandomItemFrom } from "../utils/random"; import { shuffle } from "../utils/shuffle"; export const getListOfRandomNotes = (count: number): Note[] => { @@ -8,6 +9,10 @@ export const getListOfRandomNotes = (count: number): Note[] => { return randomNotes.slice(undefined, count); }; -export const isValidCountList = (count: number): boolean => { +export const isValidNoteCountList = (count: number): boolean => { return count >= NOTES_LIST_MIN && count <= NOTES_LIST_MAX } + +export const getRandomNote = (): Note => { + return getRandomItemFrom(notes); +}; diff --git a/src/services/stringService.ts b/src/services/stringService.ts index d5da0ea..39f4501 100644 --- a/src/services/stringService.ts +++ b/src/services/stringService.ts @@ -1,5 +1,6 @@ import { GuitarString, guitarStrings } from "../config"; +import { getRandomItemFrom } from "../utils/random"; export const getRandomString = (): GuitarString => { - return guitarStrings[Math.floor(Math.random()*guitarStrings.length)]; + return getRandomItemFrom(guitarStrings); }; diff --git a/src/utils/random.ts b/src/utils/random.ts new file mode 100644 index 0000000..4cf9ece --- /dev/null +++ b/src/utils/random.ts @@ -0,0 +1,3 @@ +export const getRandomItemFrom = (arrary: T[]): T => { + return arrary[Math.floor(Math.random()*arrary.length)]; +}; diff --git a/src/utils/shuffle.ts b/src/utils/shuffle.ts index 6cdf507..110045c 100644 --- a/src/utils/shuffle.ts +++ b/src/utils/shuffle.ts @@ -1,7 +1,7 @@ import { Note } from "../config/Notes"; export const shuffle = (array: Note[]): Note[] => { - const arrayCopy = [...array]; + const arrayCopy = [...array]; return arrayCopy.sort(() => Math.random() - 0.5); };