Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
469 changes: 463 additions & 6 deletions src/modules/create/game-editor/editors/SoundEditor.tsx

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions src/modules/create/game-editor/editors/SoundEditor/MusicGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from "react";
import { ProgressBar } from "./components/ProgressBar";
import { MusicGridProps, GridCellData } from "./types/MusicGrid.types";
import {
ScrollableContainer,
GridWithProgressContainer,
GridContainer,
GridCell,
} from "./styles/MusicGrid.styles";

export type { GridCellData, MusicGridProps };

export const MusicGrid: React.FC<MusicGridProps> = ({
gridCells,
onMouseDown,
onMouseOver,
onMouseUp,
playbackPosition,
totalLength,
maxLength,
onSeek,
}) => (
<ScrollableContainer>
<GridWithProgressContainer>
<ProgressBar
progress={playbackPosition}
onSeek={onSeek}
totalLength={totalLength}
maxLength={maxLength}
/>
<GridContainer>
{gridCells.map((cell) => (
<GridCell
key={cell.cellKey}
isActive={cell.isActive}
isPlayingColumn={cell.isPlayingColumn}
onMouseDown={() => onMouseDown(cell.row, cell.col)}
onMouseOver={() => onMouseOver(cell.row, cell.col)}
onMouseUp={() => onMouseUp(cell.row, cell.col)}
>
{cell.isNoteStart ? cell.note : ""}
</GridCell>
))}
</GridContainer>
</GridWithProgressContainer>
</ScrollableContainer>
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { ControlButtonsProps } from "../types/SoundEditor.types";
import { ControlButtonsContainer, StyledButton } from "../styles/SoundEditor.styles";

export const ControlButtons: React.FC<ControlButtonsProps> = ({
isPlaying,
onPlay,
onStop,
onClear,
}) => (
<ControlButtonsContainer>
<StyledButton onClick={isPlaying ? onStop : onPlay}>
{isPlaying ? "Stop" : "Play"}
</StyledButton>
<StyledButton onClick={onClear}>Clear</StyledButton>
</ControlButtonsContainer>
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import { Box } from "@mui/material";
import { InstrumentButtonsProps } from "../types/SoundEditor.types";
import { ButtonContainer, StyledButton } from "../styles/SoundEditor.styles";

export const InstrumentButtons: React.FC<InstrumentButtonsProps> = ({
instruments,
currentInstrument,
onInstrumentSelect,
customInstruments,
onEdit,
onDelete,
}) => {
const isCustomInstrument = customInstruments.has(currentInstrument);

return (
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }}>
<ButtonContainer>
{Array.from(instruments.keys()).map((instrument) => (
<StyledButton
selected={currentInstrument === instrument}
key={instrument}
onClick={() => onInstrumentSelect(instrument)}
style={{ width: "100%" }}
>
{instruments.get(instrument)}
</StyledButton>
))}
</ButtonContainer>
{isCustomInstrument && onEdit && onDelete && (
<Box sx={{ display: "flex", gap: 1, mt: 1 }}>
<StyledButton
onClick={() => onEdit(currentInstrument)}
style={{ backgroundColor: "#4caf50", borderColor: "#45a049" }}
>
Edit
</StyledButton>
<StyledButton
onClick={() => onDelete(currentInstrument)}
style={{ backgroundColor: "#f44336", borderColor: "#da190b" }}
>
Delete
</StyledButton>
</Box>
)}
</Box>
);
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import { MusicSelectionButtonsProps } from "../types/SoundEditor.types";
import { MusicSelectionContainer, MusicSelectionButton } from "../styles/SoundEditor.styles";

export const MusicSelectionButtons: React.FC<MusicSelectionButtonsProps> = ({
musics,
selectedMusicIndex,
onMusicSelect,
}) => (
<MusicSelectionContainer>
{musics.map((_, index) => (
<MusicSelectionButton
selected={selectedMusicIndex === index}
key={index}
onClick={() => onMusicSelect(index)}
>
{index + 1}
</MusicSelectionButton>
))}
</MusicSelectionContainer>
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";
import { ProgressBarProps } from "../types/SoundEditor.types";
import {
ProgressBarContainer,
ProgressBarTrack,
ProgressBarFill,
ProgressBarThumb,
} from "../styles/ProgressBar.styles";

export const ProgressBar: React.FC<ProgressBarProps> = ({
progress,
onSeek,
totalLength,
maxLength,
}) => {
const handleClick = (e: React.MouseEvent<HTMLDivElement>): void => {
const rect = e.currentTarget.getBoundingClientRect();
const x = e.clientX - rect.left;
const columnWidth = rect.width / totalLength;
const column = Math.floor(x / columnWidth);
const position = Math.max(0, Math.min(maxLength - 1, column));
onSeek(position);
};

const cappedProgress = Math.min(progress, maxLength - 1);
const columnCenterPosition = totalLength > 0 ? ((cappedProgress + 0.5) / totalLength) * 100 : 0;
const fillPercentage = totalLength > 0 && maxLength > 0 ? ((cappedProgress + 1) / maxLength) * (maxLength / totalLength) * 100 : 0;

return (
<ProgressBarContainer onClick={handleClick}>
<ProgressBarTrack>
<ProgressBarFill width={`${fillPercentage}%`} />
<ProgressBarThumb left={`${columnCenterPosition}%`} />
</ProgressBarTrack>
</ProgressBarContainer>
);
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const defaultInstruments: Map<string, string> = new Map([
["piano", "Piano"],
["guitar", "Guitar"],
["flute", "Flute"],
["trumpet", "Trumpet"],
["contrabass", "Contrabass"],
["harmonica", "Harmonica"],
]);

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { styled } from "@mui/material/styles";

export const ScrollableContainer = styled("div")(() => ({
maxWidth: "45%",
maxHeight: "100%",
overflowX: "auto",
overflowY: "hidden",
display: "flex",
flexDirection: "column",
flexWrap: "nowrap",
}));

export const GridWithProgressContainer = styled("div")(() => ({
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
}));

export const GridContainer = styled("div")(() => ({
width: "max-content",
display: "grid",
gridTemplateColumns: "repeat(32, 35px)",
gridTemplateRows: "repeat(24, 20px)",
marginTop: "1em",
rowGap: "2px",
backgroundColor: "#537D8D",
boxSizing: "border-box",
border: "3px solid #537D8D",
}));

export const GridCell = styled("div")<{ isActive?: boolean; isPlayingColumn?: boolean }>(({ isActive, isPlayingColumn }) => ({
width: "35px",
height: "20px",
boxSizing: "border-box",
backgroundColor: isPlayingColumn
? "rgba(0, 188, 212, 0.4)"
: isActive
? "#2a3c45"
: "#3a5863",
color: isActive ? "black" : "transparent",
userSelect: "none",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: isActive ? "12px" : "10px",
fontWeight: isActive ? "bold" : "normal",
borderLeft: isPlayingColumn ? "3px solid #00BCD4" : "none",
borderRight: isPlayingColumn ? "3px solid #00BCD4" : "none",
boxShadow: isPlayingColumn ? "0 0 8px rgba(0, 188, 212, 0.8)" : "none",
transition: isPlayingColumn ? "background-color 0.1s ease, box-shadow 0.1s ease" : "none",
"&:hover": {
backgroundColor: isPlayingColumn
? "rgba(0, 188, 212, 0.5)"
: "#2a3c45",
},
}));

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { styled } from "@mui/material/styles";

export const ProgressBarContainer = styled("div")(() => ({
width: "1120px",
height: "30px",
marginTop: "10px",
marginBottom: "10px",
position: "relative",
cursor: "pointer",
display: "flex",
alignItems: "center",
marginLeft: "3px",
}));

export const ProgressBarTrack = styled("div")(() => ({
width: "100%",
height: "8px",
backgroundColor: "#3a5863",
borderRadius: "4px",
position: "relative",
overflow: "hidden",
}));

export const ProgressBarFill = styled("div")<{ width: string }>(({ width }) => ({
height: "100%",
width: width,
backgroundColor: "#00BCD4",
borderRadius: "4px",
transition: "width 0.1s linear",
}));

export const ProgressBarThumb = styled("div")<{ left: string }>(({ left }) => ({
position: "absolute",
top: "50%",
left: left,
transform: "translate(-50%, -50%)",
width: "16px",
height: "16px",
backgroundColor: "#4c7280",
borderRadius: "50%",
cursor: "pointer",
border: "2px solid #7597a4",
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.3)",
"&:hover": {
backgroundColor: "#3b5964",
transform: "translate(-50%, -50%) scale(1.2)",
},
}));

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Box } from "@mui/material";
import { styled } from "@mui/material/styles";

export const ButtonContainer = styled(Box)(() => ({
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(100px, 1fr))",
gap: "8px",
marginTop: "20px",
width: "100%",
maxWidth: "250px",
}));

export const MusicSelectionContainer = styled(Box)(() => ({
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gap: "4px",
marginTop: "20px",
width: "100%",
maxWidth: "150px",
}));

export const StyledButton = styled("button")<{ selected?: boolean }>(({ selected }) => ({
backgroundColor: selected ? "#4c7280" : "#537d8d",
color: "#ffffff",
padding: "8px 16px",
cursor: "pointer",
fontSize: "16px",
textAlign: "center",
textDecoration: "none",
display: "inline-block",
boxShadow: "none",
margin: "4px 2px",
fontFamily: "'Pixelify', 'Roboto', 'Helvetica', 'Arial', sans-serif",
borderRadius: "9.6px",
border: "2px solid #4c7280",
minWidth: "auto",
"&:hover": {
backgroundColor: "#3b5964",
},
}));

export const MusicSelectionButton = styled(StyledButton)(() => ({
padding: "4px 8px",
fontSize: "12px",
minWidth: "auto",
width: "100%",
}));

export const NewInstrumentButton = styled(StyledButton)(({ theme }) => ({
marginTop: theme.spacing(1),
}));

export const ControlButtonsContainer = styled("div")(() => ({
display: "flex",
justifyContent: "space-around",
marginTop: "20px",
}));

export const EditorContainer = styled("div")(() => ({
display: "flex",
gap: "20px",
alignItems: "flex-start",
width: "100%",
maxWidth: "100%",
overflow: "hidden",
}));

export const SoundEditorRoot = styled("div")(() => ({
width: "100%",
overflow: "hidden",
}));

export const ErrorMessage = styled("div")(() => ({
color: "red",
textAlign: "center",
marginTop: "10px",
}));

export const SoundEditorWrapper = styled("div")(() => ({
width: "fit-content",
maxWidth: "100%",
}));

Loading