Skip to content

Commit

Permalink
Add support for nested sections, closes #14, could use some better UI…
Browse files Browse the repository at this point in the history
… later
  • Loading branch information
olegbl committed Jun 10, 2024
1 parent 5713794 commit 7fc484f
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 103 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Releases
release/*.zip
CHANGELOG.md

# Logs
logs
Expand Down
12 changes: 7 additions & 5 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Mod Manager

- Finish TypeScript + Multifile support
- Easier file overrides
- Improved mod UI
- Subsections
- Rules
- Auto-complete
- Color picker
- File management
- Treat file as exported when it is being written manually or copied
- Conflict detection
- When a file is being written without being read first, show a warning
- Improve mod settings UX
- Consider supporting https://jsonforms.io/ based UI that outputs React material
- Delete any extracted files that weren't modified by mods
- Add D2RMM.getModList() API
- Return list of mods already installed during this installation
- Return list of mods yet to be installed during this installation
Expand Down
1 change: 1 addition & 0 deletions src/renderer/ModConfigTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type ModConfigFieldSection = {
id: string;
name: string;
defaultExpanded?: boolean;
children?: readonly ModConfigFieldOrSection[];
};

export type ModConfigField =
Expand Down
137 changes: 39 additions & 98 deletions src/renderer/ModSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,10 @@
import { Close } from '@mui/icons-material';
import { Box, Divider, FormGroup, IconButton, Typography } from '@mui/material';
import {
Accordion,
AccordionDetails,
AccordionSummary,
Box,
Divider,
FormGroup,
IconButton,
Typography,
styled,
} from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ModSettingsField from './ModSettingsField';
import {
ModConfigField,
ModConfigFieldOrSection,
ModConfigFieldSection,
} from './ModConfigTypes';

const StyledAccordion = styled(Accordion)(({ theme }) => ({
borderBottom: `1px solid ${theme.palette.divider}`,
'&:before': {
display: 'none',
},
}));

const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
backgroundColor: theme.palette.action.hover,
'&:hover': {
backgroundColor: theme.palette.action.focus,
},
}));

const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
borderTop: `1px solid ${theme.palette.divider}`,
}));

type FieldElement = { field: ModConfigField; element: JSX.Element };
type SectionElement = {
field: ModConfigFieldSection | null;
elements: FieldElement[];
};
import ModSettingsSection from './ModSettingsSection';

type Props = {
mod: Mod;
Expand Down Expand Up @@ -78,66 +42,43 @@ export default function ModSettings({
</IconButton>
</Box>
{mod.info.config[0]?.type === 'section' ? <Divider /> : null}
{mod.info.config
.reduce(
(agg: SectionElement[], field: ModConfigFieldOrSection) => {
if (field.type === 'section') {
const sectionElement: SectionElement = {
field,
elements: [],
};
return [...agg, sectionElement];
}
const fieldElement: FieldElement = {
field,
element: (
<ModSettingsField key={field.id} field={field} mod={mod} />
),
};
const section: SectionElement = agg[
agg.length - 1
] as SectionElement;
return [
...agg.slice(0, -1),
{
...section,
elements: [...section.elements, fieldElement],
},
];
},
[{ field: null, elements: [] }]
)
.filter((section) => section.elements.length > 0)
.map((section) => {
return (
<StyledAccordion
key={section.field?.id ?? 'default'}
defaultExpanded={section.field?.defaultExpanded ?? true}
disableGutters={true}
square={true}
elevation={0}
>
{section.field == null ? null : (
<StyledAccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="general-content"
id="general-header"
>
<Typography>{section.field.name}</Typography>
</StyledAccordionSummary>
)}
<StyledAccordionDetails
id="general-content"
sx={{
display: 'flex',
flexDirection: 'column',
}}
>
{section.elements.map((element) => element.element)}
</StyledAccordionDetails>
</StyledAccordion>
);
})}
{
// convert legacy "flat" sections into modern nested sections
mod.info.config
.reduce(
(agg: ModConfigFieldSection[], field: ModConfigFieldOrSection) => {
// handle top level fields outside and above of any section
// by appending them to a generic default section
if (agg.length === 0 && field.type !== 'section') {
return [
{
id: 'default-section',
type: 'section',
name: '',
children: [field],
} as ModConfigFieldSection,
];
}
// handle top level fields outside of any section
// by appending them to the preceding section
if (field.type !== 'section') {
return [
...agg.slice(0, -1),
{
...agg[agg.length - 1],
children: [...(agg[agg.length - 1].children ?? []), field],
},
];
}
// the rest should be sections
return [...agg, field];
},
[]
)
.map((section) => (
<ModSettingsSection key={section.id} section={section} mod={mod} />
))
}
</FormGroup>
);
}
87 changes: 87 additions & 0 deletions src/renderer/ModSettingsSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
Accordion,
AccordionDetails,
AccordionSummary,
Divider,
Typography,
styled,
} from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import {
ModConfigFieldOrSection,
ModConfigFieldSection,
} from './ModConfigTypes';
import ModSettingsField from './ModSettingsField';

const StyledAccordion = styled(Accordion)(({ theme }) => ({
borderBottom: `1px solid ${theme.palette.divider}`,
'&:before': {
display: 'none',
},
}));

const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
backgroundColor: theme.palette.action.hover,
'&:hover': {
backgroundColor: theme.palette.action.focus,
},
}));

const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
borderTop: `1px solid ${theme.palette.divider}`,
}));

type Props = {
section: ModConfigFieldSection;
mod: Mod;
};

export default function ModSettingsSection({
section,
mod,
}: Props): JSX.Element | null {
return (
<StyledAccordion
key={section.id ?? 'default'}
defaultExpanded={section.defaultExpanded ?? true}
disableGutters={true}
square={true}
elevation={0}
>
{section.name === '' ? null : (
<StyledAccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="general-content"
id="general-header"
>
<Typography>{section.name}</Typography>
</StyledAccordionSummary>
)}
<StyledAccordionDetails
id="general-content"
sx={{
display: 'flex',
flexDirection: 'column',
}}
>
{section.children?.reduce(
(agg: JSX.Element[], field: ModConfigFieldOrSection) => {
const addDivider =
field.type === 'section' &&
agg[agg.length - 1]?.type !== ModSettingsSection;
return [
...agg,
addDivider ? <Divider key={`divider:${field.id}`} /> : null,
field.type === 'section' ? (
<ModSettingsSection key={field.id} section={field} mod={mod} />
) : (
<ModSettingsField key={field.id} field={field} mod={mod} />
),
].filter(Boolean) as JSX.Element[];
},
[]
)}
</StyledAccordionDetails>
</StyledAccordion>
);
}

0 comments on commit 7fc484f

Please sign in to comment.