// 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 React, { SyntheticEvent,Component } from 'react'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import { PiPedalModel, PiPedalModelFactory, PresetIndexEntry, PresetIndex } from './PiPedalModel'; import Button from "@mui/material/Button"; import ButtonBase from "@mui/material/ButtonBase"; import DialogEx from './DialogEx'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import DraggableGrid, { ScrollDirection } from './DraggableGrid'; import MoreVertIcon from '@mui/icons-material//MoreVert'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; import Fade from '@mui/material/Fade'; import UploadDialog from './UploadDialog'; import SelectHoverBackground from './SelectHoverBackground'; import CloseIcon from '@mui/icons-material//Close'; import ArrowBackIcon from '@mui/icons-material//ArrowBack'; import EditIcon from '@mui/icons-material//Edit'; import RenameDialog from './RenameDialog'; import Slide, {SlideProps} from '@mui/material/Slide'; import { createStyles, Theme } from '@mui/material/styles'; import { WithStyles, withStyles} from '@mui/styles'; interface PresetDialogProps extends WithStyles { show: boolean; isEditDialog: boolean; onDialogClose: () => void; }; interface PresetDialogState { presets: PresetIndex; showActionBar: boolean; selectedItem: number; renameOpen: boolean; moreMenuAnchorEl: HTMLElement | null; openUploadDialog: boolean; }; const styles = (theme: Theme) => createStyles({ dialogAppBar: { position: 'relative', top: 0, left: 0 }, dialogActionBar: { position: 'relative', top: 0, left: 0, background: "black" }, dialogTitle: { marginLeft: theme.spacing(2), flex: 1, }, itemFrame: { display: "flex", flexDirection: "row", flexWrap: "nowrap", width: "100%", height: "56px", alignItems: "center", textAlign: "left", justifyContent: "center", paddingLeft: 8 }, iconFrame: { flex: "0 0 auto", }, itemIcon: { width: 24, height: 24, margin: 12, opacity: 0.6 }, itemLabel: { flex: "1 1 1px", marginLeft: 8 } }); const Transition = React.forwardRef(function Transition( props: SlideProps, ref: React.Ref ) { return (); }); const PresetDialog = withStyles(styles, { withTheme: true })( class extends Component { model: PiPedalModel; constructor(props: PresetDialogProps) { super(props); this.model = PiPedalModelFactory.getInstance(); this.handleDialogClose = this.handleDialogClose.bind(this); let presets = this.model.presets.get(); this.state = { presets: presets, showActionBar: false, selectedItem: presets.selectedInstanceId, renameOpen: false, moreMenuAnchorEl: null, openUploadDialog: false }; this.handlePresetsChanged = this.handlePresetsChanged.bind(this); } selectItemAtIndex(index: number) { let instanceId = this.state.presets.presets[index].instanceId; this.setState({ selectedItem: instanceId }); } isEditMode() { return this.state.showActionBar || this.props.isEditDialog; } onMoreClick(e: SyntheticEvent): void { this.setState({ moreMenuAnchorEl: e.currentTarget as HTMLElement }) } handleDownloadPreset() { this.handleMoreClose(); this.model.download("downloadPreset", this.state.selectedItem); } handleUploadPreset() { this.handleMoreClose(); this.setState({ openUploadDialog: true }); } handleMoreClose(): void { this.setState({ moreMenuAnchorEl: null }); } handlePresetsChanged() { let presets = this.model.presets.get(); if (!presets.areEqual(this.state.presets, false)) // avoid a bunch of peculiar effects if we update while a drag is in progress { // if we don't have a valid selection, then use the current preset. if (this.state.presets.getItem(this.state.selectedItem) == null) { this.setState({ presets: presets, selectedItem: presets.selectedInstanceId }); } else { this.setState({ presets: presets }); } } } componentDidMount() { this.model.presets.addOnChangedHandler(this.handlePresetsChanged); this.handlePresetsChanged(); // scroll selected item into view. } componentWillUnmount() { this.model.presets.removeOnChangedHandler(this.handlePresetsChanged); } getSelectedIndex() { let instanceId = this.isEditMode() ? this.state.selectedItem : this.state.presets.selectedInstanceId; let presets = this.state.presets; for (let i = 0; i < presets.presets.length; ++i) { if (presets.presets[i].instanceId === instanceId) return i; } return -1; } handleDeleteClick() { if (!this.state.selectedItem) return; let selectedItem = this.state.selectedItem; if (selectedItem !== -1) { this.model.deletePresetItem(selectedItem) .then((selectedItem: number) => { this.setState({ selectedItem: selectedItem }); }) .catch((error) => { this.model.showAlert(error); }); } } handleDialogClose() { this.props.onDialogClose(); } handleItemClick(instanceId: number): void { if (this.isEditMode()) { this.setState({ selectedItem: instanceId }); } else { this.model.loadPreset(instanceId); this.props.onDialogClose(); } } showActionBar(show: boolean): void { this.setState({ showActionBar: show }); } mapElement(el: any): React.ReactNode { let presetEntry = el as PresetIndexEntry; let classes = this.props.classes; let selectedItem = this.isEditMode() ? this.state.selectedItem : this.state.presets.selectedInstanceId; return (
this.handleItemClick(presetEntry.instanceId)} >
{presetEntry.name}
); } updateServerPresets(newPresets: PresetIndex) { newPresets = newPresets.clone(); this.model.updatePresets(newPresets) .catch((error) => { this.model.showAlert(error); }); } moveElement(from: number, to: number): void { let newPresets = this.state.presets.clone(); newPresets.movePreset(from, to); this.setState({ presets: newPresets, selectedItem: newPresets.presets[to].instanceId }); this.updateServerPresets(newPresets); } getSelectedName(): string { let item = this.state.presets.getItem(this.state.selectedItem); if (item) return item.name; return ""; } handleRenameClick() { let item = this.state.presets.getItem(this.state.selectedItem); if (item) { this.setState({ renameOpen: true }); } } handleRenameOk(text: string) { let item = this.state.presets.getItem(this.state.selectedItem); if (!item) return; if (item.name !== text) { this.model.renamePresetItem(this.state.selectedItem, text) .catch((error) => { this.onError(error); }); } this.setState({ renameOpen: false }); } handleCopy() { let item = this.state.presets.getItem(this.state.selectedItem); if (!item) return; this.model.duplicatePreset(this.state.selectedItem) .then((newId) => { this.setState({ selectedItem: newId }); }).catch((error) => { this.onError(error); }); } onError(error: string): void { this.model?.showAlert(error); } render() { let classes = this.props.classes; let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar; let defaultSelectedIndex = this.getSelectedIndex(); return ( { this.handleDialogClose() }} TransitionComponent={Transition} style={{userSelect: "none"}}>
Presets this.showActionBar(true)} > { e.stopPropagation(); e.preventDefault(); }} > {(!this.props.isEditDialog) ? ( this.showActionBar(false)} aria-label="close"> ) : ( )} Presets {(this.state.presets.getItem(this.state.selectedItem) != null) && (
{ this.setState({ renameOpen: false }) }} onOk={(text: string) => { this.handleRenameOk(text); } } /> this.handleDeleteClick()} > Delete { this.onMoreClick(e) }} > this.handleMoreClose()} TransitionComponent={Fade} > { this.handleDownloadPreset(); }} > Download preset { this.handleUploadPreset() }}> Upload preset
) }
this.showActionBar(true)} canDrag={this.isEditMode()} onDragStart={(index, x, y) => { this.selectItemAtIndex(index) }} moveElement={(from, to) => { this.moveElement(from, to); }} scroll={ScrollDirection.Y} defaultSelectedIndex={defaultSelectedIndex} > { this.state.presets.presets.map((element) => { return this.mapElement(element); }) }
this.setState({selectedItem: instanceId}) } uploadAfter={this.state.selectedItem} open={this.state.openUploadDialog} onClose={() => { this.setState({ openUploadDialog: false }) }} />
); } } ); export default PresetDialog;