diff --git a/apps/react/src/App.tsx b/apps/react/src/App.tsx index 89f6c335..1d4f06d7 100644 --- a/apps/react/src/App.tsx +++ b/apps/react/src/App.tsx @@ -4,7 +4,8 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { StudyScreen } from './components'; import { MidiToRedux } from './components/MidiToRedux'; import { AuthenticatedRoute } from './components/navigation/Routers'; -import { CoursesScreen, NotationInputScreen } from './screens'; +import { CoursesScreen } from './screens/CoursesScreen'; +import { NotationInputScreen } from './screens/NotationInputScreen'; import { AllDeckCardsScreen } from './screens/AllDeckCardsScreen'; import { DecksScreen } from './screens/DecksScreen'; import { DeckPreviewScreen } from './screens/DeckPreviewScreen'; diff --git a/apps/react/src/components/DebugValidator.tsx b/apps/react/src/components/DebugValidator.tsx deleted file mode 100644 index 056104e2..00000000 --- a/apps/react/src/components/DebugValidator.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react'; -import { useAppSelector } from 'MemoryFlashCore/src/redux/store'; -import { MultiSheetCard } from 'MemoryFlashCore/src/types/MultiSheetCard'; -import { buildScoreTimeline, activeNotesAt } from 'MemoryFlashCore/src/lib/scoreTimeline'; -import { Midi } from 'tonal'; - -interface DebugValidatorProps { - card: MultiSheetCard; -} - -export const DebugValidator: React.FC = ({ card }) => { - return null; - const onNotes = useAppSelector((s) => s.midi.notes); - const wrongNotes = useAppSelector((s) => s.midi.wrongNotes); - const waiting = useAppSelector((s) => s.midi.waitingUntilEmpty); - const index = useAppSelector((s) => s.scheduler.multiPartCardIndex); - - const timeline = React.useMemo(() => buildScoreTimeline(card.question), [card.question]); - const expectedNotes = React.useMemo(() => activeNotesAt(timeline, index), [timeline, index]); - - const onNotesMidi = onNotes.map((n) => n.number); - const wrongNotesList = wrongNotes; - - return ( -
-
-

🎹 Validation Debug

- -
- Current Index: {index} -
- -
- Waiting: {waiting ? 'YES' : 'NO'} -
-
- -
- Expected Notes: -
- {expectedNotes.length === 0 ? ( - (rest) - ) : ( - expectedNotes.map((n) => ( -
- {Midi.midiToNoteName(n.midi)} ({n.midi}) -
- )) - )} -
-
- -
- Currently Playing: -
- {onNotesMidi.length === 0 ? ( - (none) - ) : ( - onNotesMidi.map((midi) => ( -
- {Midi.midiToNoteName(midi)} ({midi}) -
- )) - )} -
-
- -
- Wrong Notes: -
- {wrongNotesList.length === 0 ? ( - (none) - ) : ( - wrongNotesList.map((midi) => ( -
- {Midi.midiToNoteName(midi)} ({midi}) -
- )) - )} -
-
- -
- Timeline Beats: -
- [{timeline.beats.map((b) => b.toFixed(2)).join(', ')}] -
-
- -
- Timeline Events: -
- {timeline.events.map((event, i) => ( -
- {Midi.midiToNoteName(event.midi)} ({event.midi}) Voice {event.voice}:{' '} - {event.start}-{event.end} -
- ))} -
-
- - {/*
-
- Validation Status:{' '} -
-
*/} -
- ); -}; diff --git a/apps/react/src/components/Dropdown.tsx b/apps/react/src/components/Dropdown.tsx index 61d0010d..6a97bda4 100644 --- a/apps/react/src/components/Dropdown.tsx +++ b/apps/react/src/components/Dropdown.tsx @@ -1,12 +1,7 @@ -import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'; +import { Menu, MenuButton } from '@headlessui/react'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; -import clsx from 'clsx'; -import React, { Fragment } from 'react'; - -interface DropdownItem { - label: string; - onClick: () => void; -} +import React from 'react'; +import { DropdownMenu, DropdownItem } from './DropdownMenu'; interface DropdownProps { label: React.ReactNode; @@ -14,49 +9,17 @@ interface DropdownProps { onButtonClick?: (e: React.MouseEvent | undefined) => void; } -const Dropdown: React.FC = ({ label, items, onButtonClick }) => { - return ( - -
- - {label} - -
- - -
- {items.map((item, index) => ( - - {({ focus }) => ( - - )} - - ))} -
-
-
-
- ); -}; +const Dropdown: React.FC = ({ label, items, onButtonClick }) => ( + + + {label} + + + +); export default Dropdown; diff --git a/apps/react/src/components/FlashCardDeleteButton.tsx b/apps/react/src/components/FlashCardDeleteButton.tsx deleted file mode 100644 index 73e26966..00000000 --- a/apps/react/src/components/FlashCardDeleteButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { TrashIcon } from '@heroicons/react/24/outline'; -import React from 'react'; -import { deleteCard } from 'MemoryFlashCore/src/redux/actions/delete-card-action'; -import { CardWithAttempts } from 'MemoryFlashCore/src/redux/selectors/currDeckCardsWithAttempts'; -import { useAppDispatch } from 'MemoryFlashCore/src/redux/store'; -import { useIsCardOwner } from '../utils/useIsCardOwner'; -import { CircleHover } from './ui/CircleHover'; -import { ConfirmModal } from './modals/ConfirmModal'; - -interface FlashCardDeleteButtonProps { - card: CardWithAttempts; - show?: boolean; -} - -export const FlashCardDeleteButton: React.FC = ({ card, show }) => { - const isOwner = useIsCardOwner(card); - const dispatch = useAppDispatch(); - const [open, setOpen] = React.useState(false); - if (!show || !isOwner) return null; - return ( - <> -
- setOpen(true)}> - - -
- setOpen(false)} - message="Delete this card?" - onConfirm={() => dispatch(deleteCard(card._id))} - /> - - ); -}; diff --git a/apps/react/src/components/FlashCardEditButton.tsx b/apps/react/src/components/FlashCardEditButton.tsx deleted file mode 100644 index bc74b6a7..00000000 --- a/apps/react/src/components/FlashCardEditButton.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { PencilSquareIcon } from '@heroicons/react/24/outline'; -import { CardWithAttempts } from 'MemoryFlashCore/src/redux/selectors/currDeckCardsWithAttempts'; -import { useIsCardOwner } from '../utils/useIsCardOwner'; -import { CircleHover } from './ui/CircleHover'; - -interface FlashCardEditButtonProps { - card: CardWithAttempts; - show?: boolean; -} - -export const FlashCardEditButton: React.FC = ({ card, show }) => { - const isOwner = useIsCardOwner(card); - if (!show || !isOwner) return null; - - return ( -
- - - -
- ); -}; diff --git a/apps/react/src/components/SegmentButton.tsx b/apps/react/src/components/SegmentButton.tsx deleted file mode 100644 index 7230f558..00000000 --- a/apps/react/src/components/SegmentButton.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import clsx from 'clsx'; - -interface SegmentedButtonProps { - text: string; - Icon?: (props: { color: string }) => JSX.Element; - active: boolean; - onClick: () => void; - variant?: 'default' | 'compact'; -} - -export const SegmentButton: React.FC = ({ - text, - Icon, - active, - onClick, - variant = 'default', -}) => { - return ( - - ); -}; diff --git a/apps/react/src/components/index.ts b/apps/react/src/components/index.ts index 466c0fa8..becac626 100644 --- a/apps/react/src/components/index.ts +++ b/apps/react/src/components/index.ts @@ -22,4 +22,3 @@ export * from './CardOptionsMenu'; export * from './ConsoleErrorsButton'; export * from './TranspositionSelector'; export * from './notation'; -export * from './DebugValidator'; diff --git a/apps/react/src/components/navigation/Routers.tsx b/apps/react/src/components/navigation/Routers.tsx index c0dfa028..cf8e84ed 100644 --- a/apps/react/src/components/navigation/Routers.tsx +++ b/apps/react/src/components/navigation/Routers.tsx @@ -17,27 +17,3 @@ export const AuthenticatedRoute: React.FC = ({ screen }) => { return ; } }; - -export const UnauthenticatedRoute: React.FC = ({ screen }) => { - const isAuthenticated = useAppSelector(authSelector); - - if (isAuthenticated === 'Authenticated') { - return ; - } else if (isAuthenticated === 'PartiallyAuthenticated') { - return ; - } else { - return screen; - } -}; - -export const PartiallyAuthenticatedRoute: React.FC = ({ screen }) => { - const isAuthenticated = useAppSelector(authSelector); - - if (isAuthenticated === 'Authenticated') { - return ; - } else if (isAuthenticated === 'PartiallyAuthenticated') { - return screen; - } else { - return ; - } -}; diff --git a/apps/react/src/components/ui/Button.tsx b/apps/react/src/components/ui/Button.tsx index 0ebd1084..5709945b 100644 --- a/apps/react/src/components/ui/Button.tsx +++ b/apps/react/src/components/ui/Button.tsx @@ -1,54 +1,32 @@ import React from 'react'; import clsx from 'clsx'; import { Spinner } from '../feedback/Spinner'; +import { + ButtonVariant, + buttonBaseClasses, + variantEnabledStyles, + variantDisabledStyles, +} from './buttonStyles'; -export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'danger'; +export type { ButtonVariant }; export type ButtonProps = React.ButtonHTMLAttributes & { loading?: boolean; variant?: ButtonVariant; }; -const variantStyles: Record = { - primary: { - enabled: - 'bg-accent text-white hover:bg-accent-hover dark:bg-[#e8e8ea] dark:text-[#1a1a1a] dark:hover:bg-[#d4d4d6] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent', - disabled: - 'bg-gray-300 text-gray-500 cursor-not-allowed dark:bg-gray-700 dark:text-gray-500', - }, - secondary: { - enabled: - 'bg-gray-100 text-lm-fg hover:bg-gray-200 dark:bg-dm-elevated dark:text-dm-fg dark:hover:bg-white/15', - disabled: - 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-dm-surface dark:text-gray-500', - }, - outline: { - enabled: - 'bg-transparent text-lm-muted hover:bg-gray-100 dark:text-dm-muted dark:hover:bg-white/5', - disabled: 'bg-transparent text-gray-300 cursor-not-allowed dark:text-gray-600', - }, - danger: { - enabled: - 'bg-red-600 text-white hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600', - disabled: 'bg-red-300 text-red-100 cursor-not-allowed', - }, -}; - export const Button = React.forwardRef( ( { className = '', children, loading = false, disabled, variant = 'primary', ...props }, ref, ) => { const isDisabled = loading || disabled; - const baseClasses = - 'inline-flex justify-center items-center rounded-md px-4 py-2 text-sm font-medium transition-all duration-150'; - const styles = variantStyles[variant]; return (