Skip to content

Commit

Permalink
App design rework (#21)
Browse files Browse the repository at this point in the history
* Add note context and redesign note

* Add chord context [broken]

* Add chord context

* App redesign
  • Loading branch information
cyrilgourgouillon committed Nov 27, 2023
1 parent 6211d41 commit 7981d0d
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 207 deletions.
30 changes: 18 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Button, ChakraProvider } from "@chakra-ui/react";
import { ChordsPage, NotesPage } from "./pages";
import { AppPages } from "./config";
import { useState } from "react";
import { Button, ChakraProvider } from '@chakra-ui/react';
import { ChordsPage, NotesPage } from './pages';
import { AppPages } from './config';
import { useState } from 'react';
import { NoteSettingsContextProvider } from './contexts/NoteContext';
import { ChordSettingsContextProvider } from './contexts/ChordContext';

const App = () => {
const [selectedPage, setSelectedPage] = useState(AppPages.notes);
Expand All @@ -19,15 +21,19 @@ const App = () => {
Switch to notes
</Button>
)}
{selectedPage === AppPages.notes && <NotesPage />}
{selectedPage === AppPages.chords && <ChordsPage />}
{selectedPage === AppPages.notes && (
<NoteSettingsContextProvider>
<NotesPage />
</NoteSettingsContextProvider>
)}
{selectedPage === AppPages.chords && (
<ChordSettingsContextProvider>
<ChordsPage />
</ChordSettingsContextProvider>
)}
<div className="mb-5">
{"Made with ❤️ by "}
<a
href="https://github.com/cyrilgourgouillon"
target="_blank"
className="text-red-700"
>
{'Made with ❤️ by '}
<a href="https://github.com/cyrilgourgouillon" target="_blank" className="text-red-700">
Cyril Gourgouillon
</a>
</div>
Expand Down
43 changes: 15 additions & 28 deletions src/components/AutoSkipper.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,49 @@
import { ButtonGroup, Button, IconButton, useToast } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import { Speed, speeds } from "../config";
import { getSpeedColor, getSpeedIcon } from "../services";
import { ButtonGroup, IconButton, useToast } from '@chakra-ui/react';
import { useEffect, useState } from 'react';
import { Speed, speeds } from '../config';
import { getSpeedColor, getSpeedIcon } from '../services';

export const AutoSkipper = ({ onSkip }: { onSkip: () => void }) => {
const toast = useToast();
const [speed, setSpeed] = useState<Speed | undefined>(undefined);
const [intervalId, setIntervalId] = useState<number | undefined>();

useEffect(() => {
if (speed) {
const skipInterval = setInterval(() => {
onSkip();
}, speed);

setIntervalId(skipInterval);

return () => clearInterval(skipInterval);
}
}, [onSkip, speed]);

const handleStopAutoSkipper = () => {
clearInterval(intervalId);
setSpeed(undefined);
};

const triggerToast = (skipDuration: Speed) => {
toast({
title: `Auto-skip every ${skipDuration / 1000} seconds`,
status: "info",
title: `Auto skip every ${skipDuration / 1000}"`,
status: 'info',
duration: 1000,
});
};

return (
<>
<ButtonGroup size="sm" isAttached>
<Button
className={`w-full ${speed === undefined ? "cursor-default" : ""}`}
variant="outline"
onClick={handleStopAutoSkipper}
disabled={speed === undefined}
colorScheme={speed === undefined ? "grey" : "blue"}
>
{speed === undefined ? "Auto-skipper" : "Stop"}
</Button>
<ButtonGroup isAttached className='flex justify-center'>
{speeds.map((s, i) => (
<IconButton
key={i}
aria-label="minus"
icon={getSpeedIcon(s)}
variant="outline"
onClick={() => {
triggerToast(s);
setSpeed(s);
if (speed === s) {
setSpeed(undefined);
} else {
triggerToast(s);
setSpeed(s);
}
}}
variant={'outline'}
isActive={speed === s}
colorScheme={speed === s ? getSpeedColor(s) : ""}
colorScheme={getSpeedColor(s)}
/>
))}
</ButtonGroup>
Expand Down
15 changes: 9 additions & 6 deletions src/components/ChordsList.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { Chord } from "../config";
import { chordToString } from "../services/chordService";
import { Chord } from '../config';
import { chordToString } from '../services/chordService';

export const ChordsList = ({
chords,
ShapeDecorator,
onClick,
}: {
chords: Chord[];
ShapeDecorator?: React.ReactNode;
onClick?: () => void;
}) => {
return (
<>
<div className="text-[25px] md:text-[25px] font-bold text-neutral-500">
{ShapeDecorator}
</div>
<div className="flex flex-col md:flex-row md:gap-7 text-[35px] md:text-[75px] mb-6 font-bold">
<div className="text-[25px] md:text-[25px] font-bold text-neutral-500">{ShapeDecorator}</div>
<div
onClick={onClick}
className="flex flex-col md:flex-row md:gap-7 text-[35px] md:text-[75px] mb-6 font-bold cursor-pointer select-none"
>
{chords.map((chord: Chord, index: number) => (
<div key={index}>{chordToString(chord)}</div>
))}
Expand Down
66 changes: 66 additions & 0 deletions src/components/ChordsSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
ButtonGroup,
IconButton,
Button,
PopoverContent,
Popover,
PopoverArrow,
PopoverBody,
PopoverTrigger,
} from "@chakra-ui/react";
import { FaMinus, FaPlus } from "react-icons/fa";
import { MdBuild } from "react-icons/md";
import { CHORDS_LIST_MIN, CHORDS_LIST_MAX } from "../config/constants";
import { AutoSkipper } from "./AutoSkipper";
import { useChordSettingsContext } from "../hooks";

export const ChordsSettings = () => {
const {
numberOfChordDisplayed,
getRandomChordsOnClick,
changeNumberOfChordDisplayed,
toggleShapeVisible,
} = useChordSettingsContext();

return (
<Popover>
<PopoverTrigger>
<IconButton icon={<MdBuild />} aria-label={"settings"}>
Settings
</IconButton>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverBody>
<div className="flex flex-col gap-3 items-stretch">
<ButtonGroup variant="outline">
<IconButton
aria-label="minus"
icon={<FaMinus />}
onClick={() => changeNumberOfChordDisplayed(-1)}
disabled={numberOfChordDisplayed === CHORDS_LIST_MIN}
/>
<Button onClick={getRandomChordsOnClick}>
Generate list of {numberOfChordDisplayed} chords
</Button>
<IconButton
aria-label="plus"
icon={<FaPlus />}
onClick={() => changeNumberOfChordDisplayed(1)}
disabled={numberOfChordDisplayed === CHORDS_LIST_MAX}
/>
</ButtonGroup>
<Button
variant="outline"
leftIcon={<MdBuild />}
onClick={toggleShapeVisible}
>
Toggle shape complexity
</Button>
<AutoSkipper onSkip={getRandomChordsOnClick} />
</div>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
7 changes: 6 additions & 1 deletion src/components/NotesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ import { Note } from "../config";
export const NotesList = ({
notes,
GuitarStringDecorator,
onClick,
}: {
notes: Note[];
GuitarStringDecorator?: React.ReactNode;
onClick?: () => void;
}) => {
return (
<>
<div className="text-[25px] md:text-[25px] font-bold text-neutral-500">
{GuitarStringDecorator}
</div>
<div className="flex flex-col md:flex-row md:gap-7 text-[35px] md:text-[75px] mb-6 font-bold">
<div
onClick={onClick}
className="flex flex-col flex-wrap md:flex-row md:gap-7 text-[35px] md:text-[75px] mb-6 font-bold cursor-pointer select-none"
>
{notes.map((note: Note, index: number) => (
<div key={index}>{note}</div>
))}
Expand Down
56 changes: 56 additions & 0 deletions src/components/NotesSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
ButtonGroup,
IconButton,
Button,
PopoverContent,
Popover,
PopoverArrow,
PopoverBody,
PopoverTrigger,
} from '@chakra-ui/react';
import { FaMinus, FaPlus } from 'react-icons/fa';
import { MdBuild } from 'react-icons/md';
import { NOTES_LIST_MIN, NOTES_LIST_MAX } from '../config/constants';
import { AutoSkipper } from './AutoSkipper';
import { useNoteSettingsContext } from '../hooks';

export const NotesSettings = () => {
const { numberOfNoteDisplayed, getRandomNotesOnClick, changeNumberOfNoteDisplayed, toggleStringVisible } =
useNoteSettingsContext();

return (
<Popover>
<PopoverTrigger>
<IconButton icon={<MdBuild />} aria-label={'settings'}>
Settings
</IconButton>
</PopoverTrigger>
<PopoverContent>
<PopoverArrow />
<PopoverBody>
<div className="flex flex-col gap-3 items-stretch">
<ButtonGroup variant="outline">
<IconButton
aria-label="minus"
icon={<FaMinus />}
onClick={() => changeNumberOfNoteDisplayed(-1)}
disabled={numberOfNoteDisplayed === NOTES_LIST_MIN}
/>
<Button onClick={getRandomNotesOnClick}>Generate list of {numberOfNoteDisplayed} notes</Button>
<IconButton
aria-label="plus"
icon={<FaPlus />}
onClick={() => changeNumberOfNoteDisplayed(1)}
disabled={numberOfNoteDisplayed === NOTES_LIST_MAX}
/>
</ButtonGroup>
<Button variant="outline" leftIcon={<MdBuild />} onClick={toggleStringVisible}>
Toggle string complexity
</Button>
<AutoSkipper onSkip={getRandomNotesOnClick} />
</div>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
3 changes: 3 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './NotesList';
export * from './ChordsList';
export * from './AutoSkipper';
export * from './NotesSettings'
export * from './ChordsSettings';
58 changes: 58 additions & 0 deletions src/contexts/ChordContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { createContext, useState } from 'react';
import { DEFAULT_NUMBER_OF_CHORD, } from '../config/constants';
import { getListOfRandomChords, getRandomNoteFromCaged, isValidChordCountList } from '../services';
import { CagedType, Chord } from '../config';

interface ChordSettingsContextProps {
chords: Chord[];
numberOfChordDisplayed: number;
isShapeVisible: boolean;
cagedPosition: CagedType;
getRandomChordsOnClick: () => void;
changeNumberOfChordDisplayed: (step: number) => void;
toggleShapeVisible: () => void;
}

export const ChordSettingsContext = createContext<ChordSettingsContextProps | undefined>(undefined);

export const ChordSettingsContextProvider = ({ children }: { children: React.ReactNode }) => {
const [chords, setChords] = useState<Chord[]>(getListOfRandomChords(DEFAULT_NUMBER_OF_CHORD));
const [numberOfChordDisplayed, setNumberOfChordDisplayed] = useState(DEFAULT_NUMBER_OF_CHORD);
const [isShapeVisible, setIsShapeVisible] = useState(false);
const [cagedPosition, setCagedPosition] = useState<CagedType>(getRandomNoteFromCaged());

const changeNumberOfChordDisplayed = (step: number) => {
setNumberOfChordDisplayed((prevState: number) => {
const value = prevState + step;
if (isValidChordCountList(value)) {
return value;
}
return prevState;
});
};

const toggleShapeVisible = () => {
setIsShapeVisible((prevState: boolean) => !prevState);
};

const getRandomChordsOnClick = () => {
setChords(getListOfRandomChords(numberOfChordDisplayed));
setCagedPosition(getRandomNoteFromCaged());
};

return (
<ChordSettingsContext.Provider
value={{
chords,
numberOfChordDisplayed,
isShapeVisible,
cagedPosition,
getRandomChordsOnClick,
changeNumberOfChordDisplayed,
toggleShapeVisible,
}}
>
{children}
</ChordSettingsContext.Provider>
);
};
Loading

0 comments on commit 7981d0d

Please sign in to comment.