Skip to content

Commit

Permalink
#12 Add modal dialog for adding sections, consider order of sections
Browse files Browse the repository at this point in the history
  • Loading branch information
tscz committed Jan 17, 2020
1 parent c6584c9 commit be6f174
Show file tree
Hide file tree
Showing 10 changed files with 408 additions and 102 deletions.
86 changes: 83 additions & 3 deletions src/components/dialogManagement/dialogManagement.tsx
@@ -1,10 +1,15 @@
import { FormLabel } from "@material-ui/core";
import FormControl from "@material-ui/core/FormControl";
import TextField from "@material-ui/core/TextField";
import React, { Component, FunctionComponent, useState } from "react";
import { connect } from "react-redux";

import PersistenceApi from "../../api/persistenceApi";
import { initialAnalysisState } from "../../states/analysisSlice";
import {
addedSection,
initialAnalysisState,
SectionType
} from "../../states/analysisSlice";
import { closedDialog, DialogType } from "../../states/dialogsSlice";
import {
createdProject,
Expand All @@ -13,8 +18,11 @@ import {
switchedPage
} from "../../states/projectSlice";
import store, { ApplicationState } from "../../states/store";
import ArrayUtil from "../../util/ArrayUtil";
import FileInput, { FileType } from "../fileInput/fileInput";
import MeasureSelect from "../measureSelect/measureSelect";
import ModalDialog, { DialogAction } from "../modalDialog/modalDialog";
import SectionSelect from "../sectionSelect/sectionSelect";

interface PropsFromState {
type: DialogType;
Expand All @@ -25,6 +33,7 @@ interface PropsFromDispatch {
switchedPage: typeof switchedPage;
closedDialog: typeof closedDialog;
createdProject: typeof createdProject;
addedSection: typeof addedSection;
}

type AllProps = PropsFromState & PropsFromDispatch;
Expand Down Expand Up @@ -91,8 +100,21 @@ class DialogManagement extends Component<AllProps> {
]}
></ModalDialog>
);
case DialogType.ADD_SECTION:
return (
<AddSectionDialog
onCancel={() => this.props.closedDialog()}
onSubmit={(sectionType, start, end) => {
this.props.addedSection({
measures: ArrayUtil.range(start, end),
type: sectionType
});
this.props.closedDialog();
}}
></AddSectionDialog>
);
default:
return null;
throw Error();
}
}
}
Expand Down Expand Up @@ -190,6 +212,63 @@ const OpenDialog: FunctionComponent<{
);
};

const AddSectionDialog: FunctionComponent<{
onSubmit: (sectionType: SectionType, start: number, end: number) => void;
onCancel: () => void;
}> = props => {
const [sectionType, setSectionType] = useState(SectionType.UNDEFINED);
const [start, setStart] = useState(1);
const [end, setEnd] = useState(2);
const actions: () => DialogAction[] = () => {
return [
{
label: "Cancel",
onClick: () => props.onCancel()
},
{
label: "Add",
onClick: () => props.onSubmit(sectionType, start, end),
disabled: sectionType === SectionType.UNDEFINED || start > end
}
];
};

return (
<ModalDialog
title="Add new Section"
subTitle="Please define section and add it to the song structure."
actions={actions()}
onCancel={props.onCancel}
>
<FormControl fullWidth style={{ marginBottom: "20px" }}>
<FormLabel component="legend">Section Type</FormLabel>
<SectionSelect
value={sectionType}
onChange={sectionType => setSectionType(sectionType)}
></SectionSelect>
</FormControl>
<FormControl fullWidth style={{ marginBottom: "20px" }}>
<FormLabel component="legend">First Measure</FormLabel>
<MeasureSelect
value={start}
min={0}
max={42}
onChange={value => setStart(value)}
/>
</FormControl>
<FormControl fullWidth style={{ marginBottom: "20px" }}>
<FormLabel component="legend">Last Measure</FormLabel>
<MeasureSelect
value={end}
min={0}
max={42}
onChange={value => setEnd(value)}
/>
</FormControl>
</ModalDialog>
);
};

const mapStateToProps = ({ dialog, project }: ApplicationState) => {
return {
type: dialog.currentDialog,
Expand All @@ -200,7 +279,8 @@ const mapStateToProps = ({ dialog, project }: ApplicationState) => {
const mapDispatchToProps = {
closedDialog,
switchedPage,
createdProject
createdProject,
addedSection
};

export default connect(mapStateToProps, mapDispatchToProps)(DialogManagement);
2 changes: 2 additions & 0 deletions src/components/measureSelect/measureSelect.tsx
Expand Up @@ -7,6 +7,7 @@ const MeasureSelect: FunctionComponent<{
min: number;
max: number;
onChange: (value: number) => void;
disabled?: boolean;
}> = props => {
const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
let newMeasure = event.target.value as number;
Expand All @@ -20,6 +21,7 @@ const MeasureSelect: FunctionComponent<{
style={{
minWidth: "40px"
}}
disabled={props.disabled}
>
{new Array<number>(props.max - props.min + 1)
.fill(0)
Expand Down
10 changes: 7 additions & 3 deletions src/components/sectionOverview/sectionOverview.tsx
Expand Up @@ -34,20 +34,24 @@ class SectionOverview extends React.Component<AllProps> {
{sections.allIds.map(sectionId => (
<Grid item key={sectionId}>
<Grid container direction="column">
<Grid item>
<Grid item key={sectionId + "_caption"}>
<Typography variant="caption">
{sections.byId[sectionId].type.toLowerCase()}
</Typography>
</Grid>
<Grid item>
<Grid item key={sectionId + "_body"}>
<Grid
container
direction="column"
style={{ marginBottom: "5px" }}
>
{generateMatrix(sections.byId[sectionId].measures, 8).map(
row => (
<ButtonGroup orientation="horizontal" size="small">
<ButtonGroup
key={sectionId + "_buttonGroup_" + row[0]}
orientation="horizontal"
size="small"
>
{row.map(measureId => {
let measure: Measure = measures.byId[measureId];

Expand Down
5 changes: 1 addition & 4 deletions src/pages/structure/structurePage.tsx
Expand Up @@ -114,10 +114,7 @@ class StructurePage extends React.Component<AllProps, State> {
></View>
}
topRight={
<View
title="Song Navigation"
body={<StructureNavigationView />}
></View>
<View title="Song Measures" body={<StructureNavigationView />}></View>
}
bottom={<View title="Song Structure" body={<StructureView />}></View>}
></ContentLayout>
Expand Down
29 changes: 24 additions & 5 deletions src/states/analysisSlice.test.ts
@@ -1,3 +1,4 @@
import ArrayUtil from "../util/ArrayUtil";
import reducer, {
addedSection,
AnalysisState,
Expand Down Expand Up @@ -101,13 +102,30 @@ it("can update the rhythm (only time signature)", () => {
expect(state.timeSignature).toEqual(TimeSignatureType.THREE_FOUR);
});

const initialState2: AnalysisState = {
sections: {
allIds: ["UNDEFINED_0_99"],
byId: {
UNDEFINED_0_99: {
measures: ArrayUtil.range(0, 99),
type: SectionType.UNDEFINED
}
}
},
bpm: 42,
firstMeasureStart: 3,
audioDuration: 180,
audioSampleRate: 44400,
measures: { allIds: [], byId: {} },
timeSignature: TimeSignatureType.FOUR_FOUR
};
it("can add a section", () => {
let section: Section = {
type: SectionType.BRIDGE,
measures: ["0", "1", "2", "3", "4"]
};

let state: AnalysisState = reducer(undefined, addedSection(section));
let state: AnalysisState = reducer(initialState2, addedSection(section));

expect(state.sections.allIds).toContainEqual("BRIDGE_0_4");
expect(state.sections.byId["BRIDGE_0_4"]).toEqual(section);
Expand All @@ -119,12 +137,13 @@ it("can remove a section", () => {
measures: ["0", "1", "2", "3", "4"]
};

let state: AnalysisState = reducer(undefined, addedSection(section));
let state: AnalysisState = reducer(initialState2, addedSection(section));

expect(state.sections.allIds.length).toBe(1);
expect(state.sections.allIds.length).toBe(2);
expect(state.sections.byId["BRIDGE_0_4"]).toEqual(section);

state = reducer(state, removedSection("BRIDGE_0_4"));
expect(state.sections.allIds.length).toBe(0);
expect(state.sections.byId["BRIDGE_0_4"]).toEqual(undefined);
expect(state.sections.allIds.length).toBe(1);
expect(state.sections.byId["BRIDGE_0_4"]).toBeUndefined();
expect(state.sections.byId["UNDEFINED_0_99"]).toBeDefined();
});

0 comments on commit be6f174

Please sign in to comment.