Files
op-pedal/react/src/PresetSelector.tsx
T
Robin Davies 118600204d Handle missing plugins.
Fixes #24
2022-03-11 12:52:49 -05:00

387 lines
17 KiB
TypeScript

// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import { SyntheticEvent, Component } from 'react';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel';
import SaveIconOutline from '@mui/icons-material/Save';
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import ButtonBase from "@mui/material/ButtonBase";
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { Theme } from '@mui/material/styles';
import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
import PresetDialog from './PresetDialog';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Fade from '@mui/material/Fade';
import Divider from '@mui/material/Divider';
import RenameDialog from './RenameDialog'
import Select from '@mui/material/Select';
import UploadDialog from './UploadDialog';
interface PresetSelectorProps extends WithStyles<typeof styles> {
}
interface PresetSelectorState {
presets: PresetIndex;
enabled: boolean;
showPresetsDialog: boolean;
showEditPresetsDialog: boolean;
presetsMenuAnchorRef: HTMLElement | null;
renameDialogOpen: boolean;
renameDialogDefaultName: string;
renameDialogActionName: string;
renameDialogOnOk?: (name: string) => void;
openUploadDialog: boolean;
}
const selectColor = "black";
const styles = (theme: Theme) => createStyles({
select: { // fu fu fu.Overrides for white selector on dark background.
'&:before': {
borderColor: selectColor,
},
'&:after': {
borderColor: selectColor,
},
'&:hover:not(.Mui-disabled):before': {
borderColor: selectColor,
}
},
icon: {
fill: selectColor,
},
});
const PresetSelector =
withStyles(styles, { withTheme: true })(
class extends Component<PresetSelectorProps, PresetSelectorState> {
model: PiPedalModel;
constructor(props: PresetSelectorProps) {
super(props);
this.model = PiPedalModelFactory.getInstance();
this.state = {
presets:
this.model.presets.get(),
enabled: false,
showPresetsDialog: false,
showEditPresetsDialog: false,
presetsMenuAnchorRef: null,
renameDialogOpen: false,
renameDialogDefaultName: "",
renameDialogActionName: "",
renameDialogOnOk: undefined,
openUploadDialog: false
};
this.handlePresetsChanged = this.handlePresetsChanged.bind(this);
this.handleDialogClose = this.handleDialogClose.bind(this);
this.handlePresetsMenuClose = this.handlePresetsMenuClose.bind(this);
}
handlePresetMenuClick(event: SyntheticEvent): void {
this.setState({ presetsMenuAnchorRef: (event.currentTarget as HTMLElement) });
}
handlePresetsMenuClose(): void {
this.setState({ presetsMenuAnchorRef: null });
}
handleDownloadPreset(e: SyntheticEvent) {
this.handlePresetsMenuClose();
e.preventDefault();
this.model.download("downloadPreset", this.model.presets.get().selectedInstanceId);
}
handleUploadPreset(e: SyntheticEvent) {
this.handlePresetsMenuClose();
e.preventDefault();
this.setState({ openUploadDialog: true });
}
handlePresetsMenuSave(e: SyntheticEvent): void {
this.handlePresetsMenuClose();
e.preventDefault();
this.model.saveCurrentPreset();
}
handlePresetsMenuSaveAs(e: SyntheticEvent): void {
this.handlePresetsMenuClose();
e.stopPropagation();
let currentPresets = this.model.presets.get();
let item = currentPresets.getItem(currentPresets.selectedInstanceId);
if (item == null) return;
let name = item.name;
this.renameDialogOpen(name, "Save As")
.then((newName) => {
return this.model.saveCurrentPresetAs(newName);
})
.then((newInstanceId) => {
})
.catch((error) => {
this.showError(error);
})
;
}
handlePresetsMenuRename(e: SyntheticEvent): void {
this.handlePresetsMenuClose();
e.stopPropagation();
let currentPresets = this.model.presets.get();
let item = currentPresets.getItem(currentPresets.selectedInstanceId);
if (item == null) return;
let name = item.name;
this.renameDialogOpen(name, "Rename")
.then((newName) => {
if (newName === name) return;
return this.model.renamePresetItem(this.model.presets.get().selectedInstanceId, newName);
})
.catch((error) => {
this.showError(error);
})
;
}
showError(error: string) {
this.model.showAlert(error);
}
renameDialogOpen(defaultText: string, acceptButtonText: string): Promise<string> {
let result = new Promise<string>(
(resolve, reject) => {
this.setState(
{
renameDialogOpen: true,
renameDialogDefaultName: defaultText,
renameDialogActionName: acceptButtonText,
renameDialogOnOk: (name) => {
resolve(name);
}
}
);
}
);
return result;
}
handleRenameDialogClose(): void {
this.setState({
renameDialogOpen: false,
renameDialogOnOk: undefined
});
}
handleRenameDialogOk(name: string): void {
let renameDialogOnOk = this.state.renameDialogOnOk;
this.handleRenameDialogClose();
if (renameDialogOnOk) {
renameDialogOnOk(name);
}
}
handleMenuEditPresets(): void {
this.handlePresetsMenuClose();
this.showEditPresetsDialog(true);
}
updatePresetState() {
let presets = this.model.presets.get();
let enabled = presets.presets.length > 0 && presets.selectedInstanceId !== -1;
this.setState(
{
presets: presets,
enabled: enabled,
}
);
}
handleSave() {
this.model.saveCurrentPreset();
}
handlePresetsChanged() {
this.updatePresetState();
}
componentDidMount() {
this.model.presets.addOnChangedHandler(this.handlePresetsChanged);
this.updatePresetState();
}
componentWillUnmount() {
this.model.presets.removeOnChangedHandler(this.handlePresetsChanged)
}
showPresetDialog(show: boolean) {
this.setState({
showPresetsDialog: show,
showEditPresetsDialog: false
});
}
showEditPresetsDialog(show: boolean) {
this.setState({
showPresetsDialog: show,
showEditPresetsDialog: true
});
}
handleDialogClose(): void {
this.showPresetDialog(false);
}
handleChange(event: any, extra: any): void {
// misses click on default.
// this.model.loadPreset(event.target.value as number);
}
handleSelectClose(event: any): void {
let value = event.currentTarget.getAttribute("data-value");
if (value && value.length > 0) {
this.model.loadPreset(parseInt(value));
}
//this.model.loadPreset(event.target.value as number);
}
render() {
//let classes = this.props.classes;
let presets = this.state.presets;
let classes = this.props.classes;
return (
<div style={{ marginLeft: 12, display: "flex", flexDirection: "row",
justifyContent: "left", flexWrap: "nowrap", alignItems: "center", height: "100%", position: "relative" }}>
<div style={{ flex: "1 1 auto", minWidth: 60, maxWidth: 300, position: "relative", paddingRight: 12 }} >
{true ? (
<Select variant="standard"
className={classes.select}
style={{ width: "100%", position: "relative", top: 0}} disabled={!this.state.enabled}
onChange={(e, extra) => this.handleChange(e, extra)}
onClose={(e) => this.handleSelectClose(e)}
displayEmpty
value={presets.selectedInstanceId}
inputProps={{
classes: { icon: classes.icon },
'aria-label': "Select preset"
}
}
>
{
presets.presets.map((preset) => {
return (
<MenuItem key={preset.instanceId} value={preset.instanceId} >
{(presets.presetChanged && preset.instanceId === presets.selectedInstanceId)
? (preset.name + "*")
: (preset.name)
}
</MenuItem>
);
})
}
</Select>
) : (
<ButtonBase style={{ width: "100%", textAlign: "left", borderBottom: "1px white solid", position: "relative", top: 2 }} disabled={!this.state.enabled}
onClick={(e) => { this.showPresetDialog(true); }}
>
<div style={{ width: "100%", textAlign: "left", display: "flex", flexWrap: "nowrap", flexDirection: "row", overflow: "hidden" }}>
<div style={{ flex: "0 1 auto", paddingLeft: 8, overflow: "hidden" }}>
<Typography noWrap >
{this.state.presets.getSelectedText()}
</Typography>
</div>
<div style={{ flex: "1 1 1px" }} />
<div style={{ flex: "0 0 auto" }}>
<ArrowDropDownIcon style={{ opacity: 0.75 }} />
</div>
</div>
</ButtonBase>
)}
</div>
<div style={{ flex: "0 0 auto" }}>
<IconButton
style={{ flex: "0 0 auto", opacity: this.state.presets.presetChanged ? 1.0 : 0.0 }}
onClick={(e) => { this.handleSave(); }}
size="large">
<SaveIconOutline style={{ opacity: 0.75 }} />
</IconButton>
</div>
<div style={{ flex: "0 0 auto" }}>
<IconButton
style={{ flex: "0 0 auto" }}
onClick={(e) => this.handlePresetMenuClick(e)}
size="large">
<MoreVertIcon style={{ opacity: 0.75 }} />
</IconButton>
<Menu
id="edit-presets-menu"
anchorEl={this.state.presetsMenuAnchorRef}
open={Boolean(this.state.presetsMenuAnchorRef)}
onClose={() => this.handlePresetsMenuClose()}
TransitionComponent={Fade}
>
<MenuItem onClick={(e) => this.handlePresetsMenuSave(e)}>Save preset</MenuItem>
<MenuItem onClick={(e) => this.handlePresetsMenuSaveAs(e)}>Save preset as...</MenuItem>
<MenuItem onClick={(e) => this.handlePresetsMenuRename(e)}>Rename...</MenuItem>
<Divider />
<MenuItem onClick={(e) => { this.handleDownloadPreset(e); }} >Download preset</MenuItem>
<MenuItem onClick={(e) => { this.handleUploadPreset(e) }}>Upload preset</MenuItem>
<Divider />
<MenuItem onClick={(e) => this.handleMenuEditPresets()}>Manage presets...</MenuItem>
</Menu>
</div>
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()} />
<RenameDialog open={this.state.renameDialogOpen}
defaultName={this.state.renameDialogDefaultName}
acceptActionName={this.state.renameDialogActionName}
onClose={() => this.handleRenameDialogClose()}
onOk={(name: string) => this.handleRenameDialogOk(name)} />
<UploadDialog
title='Upload preset'
extension='.piPreset'
uploadPage='uploadPreset'
onUploaded={(instanceId) => { this.model.loadPreset(instanceId); }}
open={this.state.openUploadDialog}
uploadAfter={-1}
onClose={() => { this.setState({ openUploadDialog: false }) }} />
</div>
);
}
});
export default PresetSelector;