Files
Chromacase/front/components/VirtualPiano/Octave.tsx
2023-06-17 07:01:23 +01:00

155 lines
4.0 KiB
TypeScript

import {
Note,
PianoKey,
NoteNameBehavior,
octaveKeys,
isAccidental,
HighlightedKey,
} from '../../models/Piano';
import { Box, Row, Text } from 'native-base';
import PianoKeyComp from './PianoKeyComp';
type OctaveProps = Parameters<typeof Box>[0] & {
number: number;
startNote: Note;
endNote: Note;
showNoteNames: NoteNameBehavior;
showOctaveNumber: boolean;
whiteKeyBg: string;
whiteKeyBgPressed: string;
whiteKeyBgHovered: string;
blackKeyBg: string;
blackKeyBgPressed: string;
blackKeyBgHovered: string;
highlightedNotes: Array<HighlightedKey>;
defaultHighlightColor: string;
onNoteDown: (note: PianoKey) => void;
onNoteUp: (note: PianoKey) => void;
};
const Octave = (props: OctaveProps) => {
const {
number,
startNote,
endNote,
showNoteNames,
showOctaveNumber,
whiteKeyBg,
whiteKeyBgPressed,
whiteKeyBgHovered,
blackKeyBg,
blackKeyBgPressed,
blackKeyBgHovered,
highlightedNotes,
defaultHighlightColor,
onNoteDown,
onNoteUp,
} = props;
const oK: PianoKey[] = octaveKeys.map((k) => {
return new PianoKey(k.note, number);
});
const notesArray = oK.map((k) => k.note);
const startNoteIndex = notesArray.indexOf(startNote);
const endNoteIndex = notesArray.indexOf(endNote);
const keys = oK.slice(startNoteIndex, endNoteIndex + 1);
const whiteKeys = keys.filter((k) => !isAccidental(k));
const blackKeys = keys.filter(isAccidental);
const whiteKeyWidthExpr = 'calc(100% / 7)';
const whiteKeyHeightExpr = '100%';
const blackKeyWidthExpr = 'calc(100% / 13)';
const blackKeyHeightExpr = 'calc(100% / 1.5)';
return (
<Box {...props}>
<Row height={'100%'} width={'100%'}>
{whiteKeys.map((key) => {
const highlightedKey = highlightedNotes.find((h) => h.key.note === key.note);
const isHighlighted = highlightedKey !== undefined;
const highlightColor = highlightedKey?.bgColor ?? defaultHighlightColor;
return (
// eslint-disable-next-line react/jsx-key
<PianoKeyComp
pianoKey={key}
showNoteName={showNoteNames}
bg={isHighlighted ? highlightColor : whiteKeyBg}
bgPressed={isHighlighted ? highlightColor : whiteKeyBgPressed}
bgHovered={isHighlighted ? highlightColor : whiteKeyBgHovered}
onKeyDown={() => onNoteDown(key)}
onKeyUp={() => onNoteUp(key)}
style={{
width: whiteKeyWidthExpr,
height: whiteKeyHeightExpr,
}}
/>
);
})}
{blackKeys.map((key, i) => {
const highlightedKey = highlightedNotes.find((h) => h.key.note === key.note);
const isHighlighted = highlightedKey !== undefined;
const highlightColor = highlightedKey?.bgColor ?? defaultHighlightColor;
return (
// eslint-disable-next-line react/jsx-key
<PianoKeyComp
pianoKey={key}
showNoteName={showNoteNames}
bg={isHighlighted ? highlightColor : blackKeyBg}
bgPressed={isHighlighted ? highlightColor : blackKeyBgPressed}
bgHovered={isHighlighted ? highlightColor : blackKeyBgHovered}
onKeyDown={() => onNoteDown(key)}
onKeyUp={() => onNoteUp(key)}
style={{
width: blackKeyWidthExpr,
height: blackKeyHeightExpr,
position: 'absolute',
left: `calc(calc(${whiteKeyWidthExpr} * ${
i + ((i > 1) as unknown as number) + 1
}) - calc(${blackKeyWidthExpr} / 2))`,
top: '0px',
}}
text={{
color: 'white',
fontSize: 'xs',
}}
/>
);
})}
</Row>
{showOctaveNumber && (
<Text
selectable={false}
fontSize="2xs"
color="black"
position="absolute"
bottom="0px"
left="2px"
m="2px"
>
{number}
</Text>
)}
</Box>
);
};
Octave.defaultProps = {
startNote: 'C',
endNote: 'B',
showNoteNames: 'onpress',
showOctaveNumber: false,
whiteKeyBg: 'white',
whiteKeyBgPressed: 'gray.200',
whiteKeyBgHovered: 'gray.100',
blackKeyBg: 'black',
blackKeyBgPressed: 'gray.600',
blackKeyBgHovered: 'gray.700',
highlightedNotes: [],
defaultHighlightColor: '#FF0000',
onNoteDown: () => {},
onNoteUp: () => {},
};
export default Octave;